C++获取文件夹和文件信息—_findfirst,_findnext和_findclose方法
Binhua Liu
介绍
本文的目的是介绍C++中如何通过_findfirst,和_findclose方法来查找文件夹中所有子文件夹和文件.本文还讨论了使用SHGetFileInfo函数和结构体来获取文件/文件夹的详细信息。
库和头文件
为了使用_findfirst,_findnext和_findclose方法,需要包含头文件:
io.h中包含大量对文件系统进行底层操作的函数。
本文例子中使用了字符串宏,因此还需要包含头文件:
函数族和宏定义
为了适应各种不同的编译环境,Microsoft往往对C++函数给出多个版本的实现。_findfirst,_findnext和以及_finddata_t根据以下编译环境的不同给出了多种实现
- 采用多字符集或Unicode字符集
- 采用32位时间或64位时间
- 采用32位文件长度或64位文件长度
所有函数实现列表如下:
MBCS/ Unicode |
time type |
file length type |
Functions |
MBCS |
32bit |
32bit |
_findfirst32, _findnext32, _finddata32_t |
MBCS |
32bit |
64bit |
_findfirst32i64, _findnext32i64, _finddata32i64_t |
MBCS |
64bit |
32bit |
_findfirst64i32, _findnext64i32, _finddata64i32_t |
MBCS |
64bit |
64bit |
_findfirst64, _findnext64, __finddata64_t |
Unicode |
32bit |
32bit |
_wfindfirst32, _wfindnext32, _wfinddata32_t |
Unicode |
32bit |
64bit |
_wfindfirst32i64, _wfindnext32i64, _wfinddata32i64_t |
Unicode |
64bit |
32bit |
_wfindfirst64i32, _wfindnext64i32, _wfinddata64i32_t |
Unicode |
64bit |
64bit |
_wfindfirst64, _wfindnext64, _wfinddata64_t |
_findclose方法只有一种实现。
C++同时提供了大量的宏,来保证我们的代码即使不经过修改,也可以适应不同的编译环境,这些宏全部或部分根据下面2个编译环境来决定最终被替换成哪个函数:
- 在_UNICODE被定义时,使用支持UNICODE的实现,在_MBCS被定义或2者都没有被定义时,使用支持MBCS的实现。
- 在_USE_32BIT_TIME_T 被定义时使用支持32bit时间类型的函数,在其没有被定义时采用64bit时间类型的函数。
下面列出_findfirst几种常用的宏:
宏 |
不同编译环境时对应的函数 |
_findfirst |
_findfirst32 |
_tfindfirst |
_MBCS 定义,_USE_32BIT_TIME_T 定义时: _findfirst32 _MBCS 定义,_USE_32BIT_TIME_T NOT 定义时: _findfirst64i32 _UNICODE 定义,_USE_32BIT_TIME_T 定义时: _wfindfirst32 _UNICODE 定义,_USE_32BIT_TIME_T NOT 定义时: _wfindfirst64i32 |
_tfindfirst32 |
_MBCS 定义时: _findfirst32 _UNICODE 定义时: _wfindfirst32 |
_tfindfirst64 |
_MBCS 定义时: _findfirst64 _UNICODE 定义时: _wfindfirst64 |
为了让我们的代码有很好的兼容性,下面我们都采用_tfindfirst64宏来讲解,_findfirst的其他宏和函数在用法上与其是一致的。其他用到的函数也都是宏,保证代码在多字符集和Unicode字符集下都不需修改即能通过编译。
函数用法
intptr_t _tfindfirst64 ( LPCTSTR filespec, struct _tfinddata64_t *fileinfo) |
int _tfindnext64( intptr_t handle, struct _tfinddata64_t *fileinfo ); |
int _findclose( intptr_t handle); |
上面的函数定义中,_tfindfirst64,_tfinddata64_t,_tfindnext64,LPCTSTR都是宏,LPCTSTR是字符串宏,在多字符集时等于const char*,Unicode字符集是等于const wchar*。
- _tfindfirst64函数: 该函数用于得到指定路径filespec下的第一个文件(或者文件夹), filespec支持通配符,例如"c:/*.*”指查找C盘所有的文件和子目录。 fileinfo由函数填充后返回,记录第一个文件(或文件夹)的信息,返回值是一个唯一性搜索句柄,用来传递给_tfindnext64查找下一个文件,或者传递给_findclose来关闭该句柄。如果调用失败,返回值等于-1.
- _tfindnext64函数: 该函数用于搜索下一个文件(或文件夹),参数handle是之前调用_tfindfirst64返回的句柄,fileinfo由函数填充后返回,记录当前文件(或文件夹)的信息,如果查找成功,返回值为0,否则为-1. 我们一般都采用do…while循环不断调用_tfindnext64函数来遍历当前目录下所有的子目录和文件,直到返回-1时结束。
- _findclose函数: 用于结束查找时关闭句柄,参数handle是之前调用_tfindfirst64返回的句柄。
- _tfinddata64_t 结构体: 我们以_wfinddata64_t 为例,其有如下定义
time_create,time_access,time_write分别指创建时间,最近访问时间,和最后修改时间;name为文件(或文件夹)名称;attrib描述的文件的系统属性,它由多个attributes组合而成,在MSDN中描述如下:
_A_ARCH
|
Archive. Set whenever the file is changed, and cleared by the BACKUP command. Value: 0x20 |
_A_HIDDEN
|
Hidden file. Not normally seen with the DIR command, unless the /AH option is used. Returns information about normal files as well as files with this attribute. Value: 0x02 |
_A_NORMAL
|
Normal. File can be read or written to without restriction. Value: 0x00 |
_A_RDONLY
|
Read-only. File cannot be opened for writing, and a file with the same name cannot be created. Value: 0x01 |
_A_SUBDIR
|
Subdirectory. Value: 0x10 |
_A_SYSTEM
|
System file. Not normally seen with the DIR command, unless the /AS option is used. Value: 0x04 |
_A_SUBDIR属性表示该对象是一个子目录,我们可以探测这个位是否被设置来判断这是一个文件还是文件夹。这样,我们就可以采用递归的办法,来获取每个子目录下的文件信息。
下面的是一段演示代码,采用递归的方法,获取当前目录和所有子目录下文件的信息,并把它们的文件属性打印出来,并把每个文件的全路径存储在一个vector结构中。
void GetAllFileInfo( LPCTSTR path, vector< LPCTSTR > &filesPathVector) |
_tcscat(root,_T( "//*.*" )); |
hFile=_tfindfirst64(root,&c_file); |
if (_tcslen(c_file.name)==1&&c_file.name[0]==_T( '.' ) |
||_tcslen(c_file.name)==2&&c_file.name[0]==_T( '.' )&&c_file.name[1]==_T( '.' )) |
TCHAR *fullPath = new TCHAR [MAX_PATH]; |
_tcscat(fullPath,_T( "//" )); |
_tcscat(fullPath,c_file.name); |
if (c_file.attrib&_A_SUBDIR) |
GetAllFileInfo(fullPath,filesPathVector); |
filesPathVector.push_back(fullPath); |
_tprintf(_T( "FileName: %s/r/n" ), fullPath); |
_tprintf(_T( "ReadOnly: %s/r/n" ), |
( c_file.attrib & _A_RDONLY ) ? _T( " Y " ) : _T( " N " ) ); |
_tprintf(_T( "Hidden: %s/r/n" ), |
( c_file.attrib & _A_HIDDEN ) ? _T( " Y " ) : _T( " N " ) ); |
_tprintf(_T( "System: %s/r/n" ), |
( c_file.attrib & _A_SYSTEM ) ? _T( " Y " ) : _T( " N " ) ); |
_tprintf(_T( "Arch: %s/r/n" ), |
( c_file.attrib & _A_ARCH ) ? _T( " Y " ) : _T( " N " ) ); |
_tctime64_s( timeBuffer, _countof(timeBuffer), &c_file.time_write ); |
_tprintf(_T( "WriteTime:%.24s/r/n/r/n" ),timeBuffer); |
while ( _tfindnext64( hFile, &c_file ) == 0); |
int _tmain( int argc, _TCHAR* argv[]) |
vector< LPCTSTR > filesPathVector; |
GetAllFileInfo(_T( "d://FolderForTest" ),filesPathVector); |
打印的结果:
FileName: d:/FolderForTest/a.txt
ReadOnly: N
Hidden: N
System: N
Arch: Y
WriteTime:Sun Jun 06 12:21:22 2010
FileName: d:/FolderForTest/sub1/b
ReadOnly: N
Hidden: N
System: N
Arch: Y
WriteTime:Mon Jun 07 16:33:45 2010
SHGetFileInfo方法
_tfinddata64_t结构体存储的信息很少,我们可能希望得到更多的文件信息,比如文件的类型,文件的图标等等,这就需要用到SHGetFileInfo函数:
__in DWORD dwFileAttributes, |
这是一个Windows Shell函数,需要的头文件及库为:
Header |
Shellapi.h |
Library |
Shell32.lib |
DLL |
Shell32.dll |
pszPath指定文件的路径,uFlags指定希望获取的文件信息,而获取的文件的信息通过结构体SHFILEINFO *psfi返回。我们把这段代码加在之前的代码中,示例如下:
void GetRichFileInfo( LPCTSTR path) |
SHGetFileInfo(path,0,&shfi, sizeof (shfi),SHGFI_TYPENAME|SHGFI_DISPLAYNAME|SHGFI_ATTRIBUTES); |
_tprintf(_T( "DisplayName: %s/r/n" ),shfi.szDisplayName); |
_tprintf(_T( "TypeName : %s/r/n/r/n" ),shfi.szTypeName); |
int _tmain( int argc, _TCHAR* argv[]) |
vector< LPCTSTR > filesPathVector; |
GetAllFileInfo(_T( "d://FolderForTest" ),filesPathVector); |
for ( int i=0;i<filesPathVector.size();i++) |
LPCTSTR path=filesPathVector.at(i); |
运行结果
DisplayName: a.txt
TypeName : Text Document
DisplayName: b
TypeName : File
参考:
http://msdn.microsoft.com/en-us/library/bb762179(VS.85).aspx
http://msdn.microsoft.com/en-us/library/zyzxfzac(VS.71).aspx
http://msdn.microsoft.com/en-us/library/kda16keh(VS.80).aspx
http://msdn.microsoft.com/en-us/library/6tkkkc1y(VS.80).aspx
声明:
本文为Binhua Liu原创文章,允许复制,修改,传递,转载注明出处。本文写于6/7/2007。
》点击查看原文...
博主的程序在自己机器上跑时不能显示汉字,解决方法是在函数调用前加一句setlocale(LC_ALL,"chs");这是c++国际化的手段,现在不是很了解,希望能继续学习。
如果不考虑太多因素的话,自己的代码可以做一个使用示例。 //获得dire目录下扩展名为extName的所有文件名 void getAllFileName(const string& directory, const string& extName, vector<string>& fileNames){ _finddata_t fileInfo; intptr_t hFile; string filter = directory; if(filter[filter.size()-1] != '//'){ filter.push_back('//'); } filter += "*."; filter += extName; hFile = _findfirst(filter.c_str(),&fileInfo); if(hFile == -1){ return; } do { string name(fileInfo.name); fileNames.push_back(name.substr(0,name.find_last_of('.'))); } while (_findnext(hFile,&fileInfo) == 0); _findclose(hFile); } 这里除了跟博主学习了_find一族函数的使用方法外,还学会了使用do-while 循环处理first-next模式的方法,感觉这样写代码看起来很爽。