C/C++遍历目录下的所有文件(Windows篇,超详细)
注:
1. 本文讨论的是怎么用Windows API遍历目录下的所有文件。除Windows API,还有一种Windows/Linux通用的方式,使用<io.h>。
2. 本文部分翻译自MSDN,翻译可能不准确。
WIN32_FIND_DATA结构
遍历目录下的文件需要用到WIN32_FIND_DATA结构。实际上有两种结构:WIN32_FIND_DATAA和WIN32_FIND_DATAW。A和W分别代表ASCII和宽字符(Unicode)。定义UNICODE宏时,WIN32_FIND_DATA指WIN32_FIND_DATAW;否则指WIN32_FIND_DATAA。
下面是两个结构的定义(minwinbase.h,VS2015):
typedef struct _WIN32_FIND_DATAA {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
_Field_z_ CHAR cFileName[ MAX_PATH ];
_Field_z_ CHAR cAlternateFileName[ ];
#ifdef _MAC
DWORD dwFileType;
DWORD dwCreatorType;
WORD wFinderFlags;
#endif
} WIN32_FIND_DATAA; typedef struct _WIN32_FIND_DATAW {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
_Field_z_ WCHAR cFileName[ MAX_PATH ];
_Field_z_ WCHAR cAlternateFileName[ ];
#ifdef _MAC
DWORD dwFileType;
DWORD dwCreatorType;
WORD wFinderFlags;
#endif
} WIN32_FIND_DATAW;
关于_MAC宏的部分可以忽略, 这是有历史原因的——曾今Microsoft是Mac的最大开发者,为了方便Windows上的应用移植到Mac上,就使用_MAC宏,如果是Mac操作系统_MAC就是有定义的。(根据Stack Overflow)因为这里说的是Windows,就先把这个放一边。
下面是每个结构成员的含义:
dwFileAttributes
一个文件(或路径)的文件属性。
文件属性常量:
FILE_ATTRIBUTE_ARCHIVE(0x20):文件或目录是档案文件或目录。应用程序使用这种属性标记文件,表示备份或移除。
FILE_ATTRIBUTE_COMPRESSED(0x800):文件或目录是压缩的。对于一个文件,其中的所有数据都是压缩的。对于一个目录,对于新创建的文件和子目录默认压缩。
FILE_ATTRIBUTE_DEVICE(0x40):这个值保留给系统使用。
FILE_ATTRIBUTE_DIRECTORY(0x10):表示这是一个目录。
FILE_ATTRIBUTE_ENCRYPTED(0x10):文件或目录是加密的。对于一个文件,所有的数据流都被加密了。对于一个目录,对于新创建的文件和子目录默认加密。
FILE_ATTRIBUTE_HIDDEN(0x2):文件或目录是隐藏的。遍历文件夹时一般不包括它们。
FILE_ATTRIBUTE_INTEGRITY_STREAM(0x8000):路径或用户数据流被设置为integrity(只有ReFS volume支持)。遍历文件夹时一般不包括它们。Integrity设置在文件重命名之后依然保留。如果一个文件被复制,目标文件将会是integrity,不管源文件或目标路径是否是integrity。
FILE_ATTRIBUTE_NORMAL(0x80):文件没有任何其它属性。只能单独使用。
FILE_ATTRIBUTE_NOT_CONTEXT_INDEXED(0x2000):文件或目录不会被context indexing service标索引。
FILE_ATTRIBUTE_READONLY(0x1):文件为只读。程序可以读取该文件,但不能写入或删除。此属性不适用于目录。
……(太多了,有时间再全部列举)
顺带提一下位标记(bit flags)。
如你所见,所有的文件属性常量写成二进制都只有一位是1,剩下的都是0。对于dwFileAttributes,将其写成二进制的形式,它的一些位的值是有含义的——例如个位表示是否是只读的(FILE_ATTRIBUTE_READONLY是1),16位表示是否是目录(FILE_ATTRIBUTE_DIRECTORY是0x10,即16),32位表示是否是档案文件/目录(FILE_ATTRIBUTE_ARCHIVE是0x20,即32)……
那么怎么指定多个属性呢?因为每种属性写成二进制都只有一位是1,剩下的都是0,所以可以使用按位or运算符(|)指定多个属性,例如FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE,这样每种属性对应的位都为1,其余的为0。
至于判断是否具有某个属性,可以用按位and运算符(&)。例如对于属性attrib,判断是否是目录:attrib & FILE_ATTRIBUTE_DIRECTORY。如果不为0则是目录,为0则不是。
ftCreationTime
FILETIME结构。指定一个文件或目录的创建时间。如果文件系统不支持创建时间,此成员为0。
ftLastAccessTime
FILETIME结构。对于文件,指定文件最后被读取、写入,或(对于可执行文件)被运行的时间。对于目录,指定目录的创建时间。如果文件系统不支持最后一次写入时间,此成员为0。
ftLastWriteTime
FILETIME结构。对于文件,指定文件最后被写入、截短或重写的时间(例如调用WriteFile()或SetEndOfFile()时)。日期和时间在文件属性或描述符被改变时不会被更新。
nFileSizeHigh
DWORD。文件大小(以字节为单位)的高DWORD。除非文件大小大于MAXDWORD,否则值为0。文件大小等于(nFileSizeHigh * (MAXDWORD + 1)) + nFileSizeLow。
nFileSizeLow
DWORD。文件大小(以字节为单位)的低DWORD。
dwReserved0
DWORD。如果dwFileAttributes成员含有FILE_ATTRIBUTE_REPARSE_POINT属性,这个成员指定重新分析点标签(reparse point tag)。否则这个值是未定义的。
可能的值:
IO_REPARSE_TAG_CSV,IO_REPARSE_TAG_DEDUP,IO_REPARSE_TAG_DFS,IO_REPARSE_TAG_DFSR,IO_REPARSE_TAG_HSM,IO_REPARSE_TAG_HSM2,IO_REPARSE_TAG_MOUNT_POINT,IO_REPARSE_TAG_NFS,IO_REPARSE_TAG_SIS,IO_REPARSE_TAG_SIMLINK,IO_REPARSE_TAG_WIM。
dwReserved1
DWORD。保留给将来使用。
cFileName
CHAR/WCHAR数组,大小为MAX_PATH。文件名。
cAlternateFileName
CHAR/WCHAR数组,大小为14。文件的别名。名称的格式为8.3文件名格式。
FILETIME结构
可以看到WIN32_FIND_DATA的ftCreationTime、ftLastAccessTime、ftLastWriteTime类型是FILETIME结构。那么FILETIME结构是怎样的呢?下面是MSDN上的定义:
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME;
dwLowDateTime
文件时间的低DWORD。
dwHighDateTime
文件时间的高DWORD。
FILETIME结构表示的时间(距离Epoch的秒数)为dwHighDateTime * (MAXDWORD + 1) + dwLowDateTime。
FindFirstFile()/FindNextFile()/FindClose()函数
要查找文件,需要使用FindFirstFile()、FindNextFile()和FindClose()函数。
FindFirstFile()函数
HANDLE WINAPI FindFirstFile(
_In_ LPCTSTR lpFileName,
_Out_ LPWIN32_FIND_DATA lpFindFileData
);
搜索第一个文件,创建并返回搜索句柄。
lpFileName
CHAR/WCHAR指针(取决于是否定义UNICODE)。路径或文件名。可以包含通配符,例如*或?。不能以\\字符结尾。如果以通配符、.字符或目录名结尾,用户必须有根目录和所有子目录的访问权限。(遍历目录中的所有文件时,应以*.*结尾。)
lpFindFileData
WIN32_FIND_DATA指针。用于接收找到的文件/目录的信息。
返回值
如果成功,函数将创建一个搜索句柄,可以使用该句柄调用FindNextFile()和FindClose()。如果失败,返回INVALID_HANDLE_VALUE。
FindNextFile()函数
BOOL WINAPI FindNextFile(
_In_ HANDLE hFindFile,
_Out_ LPWIN32_FIND_DATA lpFindFileData
);
搜索下一个文件。
hFindFile
HANDLE。搜索句柄。
lpFindFileData
WIN32_FIND_DATA指针。用于接收找到的文件/目录的信息。
返回值
如果成功,返回TRUE;如果失败(例如找不到更多的文件了或其它原因),返回FALSE。
FindClose()函数
BOOL WINAPI FindClose(
_Inout_ HANDLE hFindFile
);
释放搜索句柄。
hFindFile
HANDLE。搜索句柄。
返回值
如果成功,返回TRUE;如果失败,返回FALSE。
通配符(wildcards)
*和?字符被用作通配符。
指定全部具有某个扩展名的文件
格式为*.ext(ext为扩展名)。例如指定所有.txt文件:"*.txt"。指定D:\Projects\目录下所有.txt文件:"D:\\Projects\\*.txt"。
指定全部具有某个名称的文件/目录
格式为name.*(name为文件名)。例如指定所有名为readme(格式不限)的文件和目录:"readme.*"。指定D:\Projects\目录下的所有名为readme的文件:"D:\\Projects\\readme.*"。
指定具有一定长度的扩展名的文件
格式为name.???(name为文件名)。?的数量和扩展名长度一样。例如指定所有扩展名为4个字符,名为index的文件:"index.????"。
指定具有一定长度的文件名的文件
格式为???.ext(ext为扩展名)。?的数量和文件名的长度一样。例如指定所有扩展名为.txt,名字含有7个字符的文件:"???????.txt"。
当然还有更复杂的,例如*.???、????.*、*.*、????.???,分别是“所有扩展名长度为3的文件”、“所有文件名长度为4的文件/目录”、“所有文件/目录”、“所有文件名长度为4且扩展名长度为3的文件”。
当前目录和上一级目录
调用FindFirstFile()时,使用"."表示当前目录,使用".."表示上一级目录。FindFirstFile()和FindNextFile()所返回的文件/目录名也可能是"."或"..",可以忽略。
最后的工作
我们还需要声明一个HANDLE才能开始搜索:
HANDLE hFind;
程序示例
废话了这么久,也是该上程序代码了。
1. 遍历某个目录下的所有文件
遍历某个目录下的所有文件,并输出文件名和文件大小。
#include <iostream>
#include <cstring>
#include <windows.h> void listFiles(const char * dir); int main()
{
using namespace std;
char dir[];
cout << "Enter a directory (ends with \'\\\'): ";
cin.getline(dir, );
strcat(dir, "*.*"); // 需要在目录后面加上*.*表示所有文件/目录
listFiles(dir);
return ;
} void listFiles(const char * dir)
{
using namespace std;
HANDLE hFind;
WIN32_FIND_DATA findData;
LARGE_INTEGER size;
hFind = FindFirstFile(dir, &findData);
if (hFind == INVALID_HANDLE_VALUE)
{
cout << "Failed to find first file!\n";
return;
}
do
{
// 忽略"."和".."两个结果
if (strcmp(findData.cFileName, ".") == || strcmp(findData.cFileName, "..") == )
continue;
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 是否是目录
{
cout << findData.cFileName << "\t<dir>\n";
}
else
{
size.LowPart = findData.nFileSizeLow;
size.HighPart = findData.nFileSizeHigh;
cout << findData.cFileName << "\t" << size.QuadPart << " bytes\n";
}
} while (FindNextFile(hFind, &findData));
cout << "Done!\n";
}
以学校电脑为例,输入"C:\",输出如下:
$360Section <dir>
360SANDBOX <dir>
AUTOEXEC.BAT 0 bytes
boot.ini 210 bytes
bootfont.bin 322730 bytes
CONFIG.SYS 0 bytes
dell <dir>
Documents and Settings <dir>
Drivers <dir>
ExamClient <dir>
FPC <dir>
IO.SYS 0 bytes
Joinmax <dir>
ksd <dir>
MSDOS.SYS 0 bytes
MSOCache <dir>
...
(剩余输出省略)
2. 遍历某个目录里的所有文件
注意是“某个目录里”而不是“某个目录下”,两者是有区别的。“某个目录里”除了目录里的第一级的文件,还包括里面的子目录里的所有文件。
和上面的例子一样,使用listFiles()函数遍历一个目录里的所有文件。但不同的是,这里的listFiles()是递归调用的。
#include <iostream>
#include <cstring>
#include <windows.h> void listFiles(const char * dir); int main()
{
using namespace std; char dir[];
cout << "Enter a directory (do not add \'\\\' in the end): ";
cin.getline(dir, ); listFiles(dir);
return ;
} void listFiles(const char * dir)
{
using namespace std; HANDLE hFind;
WIN32_FIND_DATA findData;
LARGE_INTEGER size;
char dirNew[]; // 向目录加通配符,用于搜索第一个文件
strcpy(dirNew, dir);
strcat(dirNew, "\\*.*"); hFind = FindFirstFile(dirNew, &findData);
do
{
// 是否是文件夹,并且名称不为"."或".."
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY !=
&& strcmp(findData.cFileName, ".") !=
&& strcmp(findData.cFileName, "..") !=
)
{
// 将dirNew设置为搜索到的目录,并进行下一轮搜索
strcpy(dirNew, dir);
strcat(dirNew, "\\");
strcat(dirNew, findData.cFileName);
listFiles(dirNew);
}
else
{
size.LowPart = findData.nFileSizeLow;
size.HighPart = findData.nFileSizeHigh;
cout << findData.cFileName << "\t" << size.QuadPart << " bytes\n";
}
} while (FindNextFile(hFind, &findData)); FindClose(hFind);
}
呼,总算讲完了~如果有什么问题,请在评论中提出哦~
C/C++遍历目录下的所有文件(Windows篇,超详细)的更多相关文章
- C/C++遍历目录下的所有文件(Windows/Linux篇,超详细)
本文可转载,转载请注明出处:http://www.cnblogs.com/collectionne/p/6815924.html. 前面的一篇文章我们讲了用Windows API遍历一个目录下的所有文 ...
- windows 遍历目录下的所有文件 FindFirstFile FindNextFile
Windows下遍历文件时用到的就是FindFirstFile 和FindNextFile 首先看一下定义: HANDLE FindFirstFile( LPCTSTR lpFileName, // ...
- C 给定路径遍历目录下的所有文件
在此之前需要了解 WIN32_FIND_DATA的结构 以及 FindFirstFile. FindNextFile原型以及用法注意事项传送门如下 https://msdn.microsoft.co ...
- php 遍历目录下的所以文件和文件夹
<?php/** * 遍历文件夹和文件列 * @author lizhiming * @date 2016/06/30 */define('DS', DIRECTORY_SEPARATOR); ...
- 遍历目录下的所有文件-os.walk
#coding:utf-8 import os for root,dirs,files in os.walk("D:"): for fileItem in files: print ...
- shell 遍历目录下的所有文件
dir=/usr/local/nginx/logs for file in $dir/*; do echo $file done //结果 ./test.sh /usr/local/nginx/log ...
- shell编程--遍历目录下的文件
假定目录text下有如下文件 目录:dir_1.dir_2.dir_3 文件:text_1.text_2 遍历目录下所有的文件是目录还是文件 if -- if类型: #!bin/sh for ...
- C++遍历路径下的所有文件
intptr_t类型用于记录文件夹句柄,注意该类型不是指针类型,而是int型的重定义. _finddata_t结构体类型用于记录文件信息. _finddata_t结构体定义如下 struct _fin ...
- 复制D:\\day05目录下的所有文件到D:\\copy,并将.txt文件改为.java文件。
**解题思路: 1.首先定义一个静态的refile方法,参数传入两个文件路径 2.要复制目录下的所有文件,首先查询File类的方法,可以使用listFiles方法得到目录下的文件 3.想到这问题基本就 ...
随机推荐
- Spring Boot 整合 Redis 实现缓存操作
摘要: 原创出处 www.bysocket.com 「泥瓦匠BYSocket 」欢迎转载,保留摘要,谢谢! 『 产品没有价值,开发团队再优秀也无济于事 – <启示录> 』 本文提纲 ...
- VUE进阶(路由等)
初级教程:http://www.cnblogs.com/dmcl/p/6137469.html VUE进阶 自定义指令 http://cn.vuejs.org/v2/guide/custom-dire ...
- CSS选择器渲染效率
1 浏览器如何识别你的选择器 首先我们需要清楚,浏览器是如何读取选择器,以识别样式,并将相应的样式附于对应的HTML元素,达到美化页面的效果.Chris Coyier曾在<Efficiently ...
- Sencha+cordova 构造 华丽手机程序,并讲讲,在商用项目中经常用到的cordova插件(一)
采用 Sencha Architect 3 ,编辑设计,我只想说一个结论,jq能搞的 Sencha touch也能做到(只是 设计思路 更面向对象,学习知识量大点而已 ,不理解 MVC 就不用选了), ...
- Lambda表达式随笔
1.Lambda表达式是一个匿名函数,其本质其实还是一个函数,因此任何一个Lambda表达式都可以以其它的方式通过普通的函数实现或者代替. 2.Lambda表达式云算符:=>,该运算符读为&qu ...
- linq 为什么要用linq linq写法
LINQ,语言集成查询(Language Integrated Query)是一组用于c#和Visual Basic语言的扩展.它允许编写C#或者Visual Basic代码以查询数据库相同的方式操作 ...
- Linq 查询与普通查询的区别
普通:select * --1 from User(表名) as u --2 where u.Name like '%s%' --3 Linq : from User(表名) as u --1 whe ...
- PTA自测-3 数组元素循环右移问题
自测-3 数组元素循环右移问题 一个数组A中存有N(N>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(M≥0)个位置,即将A中的数据由(A0A1···AN-1)变换为 ...
- SQL*Plus快速入门
连接数据库sqlplus hr@\"//mymachine.mydomain:port/MYDB\" --连接到MYDB数据库的一个HR数据集里sqlplus hr@MYDB -- ...
- stl_algorithm算法之排序算法
排序算法: 注意:容器中必须重载 op< ,排序中stl标准中要求用小于来进行比较. 7.53.sort //全排序. 7.54.stable_sort //稳定排序.两个或两个以上的相邻且相等 ...