遍历文件夹
为了遍历一个文件夹下的所有文件,我们可以配对使用findfirst/findnext系列函数。在Windows平台下,它们定义于<io.h>,并由 _findfirst/_findnext 宏统一了不同平台下的名称差异性。这个宏根据当前编译环境的配置,映射到32位或64位的实现,比如在32位环境下,使用_findfirst实际调用的是_findfirst32的实现。
#ifdef _USE_32BIT_TIME_T
#define _findfirst _findfirst32
#define _findnext _findnext32
#define _findfirsti64 _findfirst32i64
#define _findnexti64 _findnext32i64
#else
#define _findfirst _findfirst64i32
#define _findnext _findnext64i32
#define _findfirsti64 _findfirst64
#define _findnexti64 _findnext64
#endif
findfirst系列函数的基础原型都是一个待查找的文件名和finddata指针。
intptr_t _findfirst(char const *filename, struct _finddata_t *finddata);
第一个参数为文件名,可以用"*.*"来匹配查找所有文件,也可以用"*.c"来查找反缀名为.c的文件。第二个参数是_finddata_t结构体指针。函数返回一个intptr_t型的变量,这是整型类型的一个别名(typedef),32位环境下它是int,64位下则是__int64,函数执行成功时返回非-1值。
对于 _finddata_t 结构体:
struct _finddata_t
{
unsigned attrib;
time_t time_create;
time_t time_access;
time_t time_write;
_fsize_t size;
char name[260];
};
time_t通常是long或者__int64,这与被选取的findfirst函数相关,_fsize_t则是unsigned long。
结构体的各个成员解释如下:
| 字段 | 解释 |
|---|---|
| attrib | 所查找文件的属性,一个文件可具有一个或多个属性的组合: _A_ARCH(存档) _A_HIDDEN(隐藏) _A_NORMAL(正常) _A_RDONLY(只读) A_SUBDIR(文件夹) _A_SYSTEM(系统) |
| time_create | 文件的创建时间 |
| time_access | 最后一次访问文件的时间 |
| time_write | 文件最后被修改的时间 |
| size | 文件大小 |
| name | 文件名 |
执行_findfirst函数后,接下来我们需要使用_findnext循环查找文件,其原型如下:
int _findnext(intptr_t handle, struct _finddata_t *finddata);
第一个参数为文件句柄,它是_findfirst函数执行成功时的返回值;第二个参数同样为_finddata_t结构体指针;若查找成功,返回0,失败返回-1。
当遍历完成后,我们需要使用findclose结束本次查找,函数原型如下:
int _findclose(intptr_t handle);
函数只有文件句柄一个参数,它是_findfirst函数执行成功时的返回值,若关闭成功返回0,失败返回-1。
可以看到,整个文件查找过程,是通过一个文件句柄(实则就是一个整数)关联起来的:findfirst用于启动一次查询操作,它返回一个称为句柄的整数,findnext利用这个句柄,得到自己目前的工作环境,继而访问文件,最后通过findclose结束本次查找。
使用流程
#include <stdio.h>
#include <io.h>
int main()
{
struct _finddata_t fd;
intptr_t handle;
// 查找当前目录下的所有文件
handle = _findfirst("*.*", &fd);
// 查找成功
if (handle != -1)
{
// 输出找到的第一个文件名
printf("%s\n", fd.name);
// 循环查找下一个文件
while (_findnext(handle, &fd) == 0)
printf("%s\n", fd.name);
}
_findclose(handle);
return 0;
}

实现dir命令
命令提示符提供了一个dir命令,作用是显示目录中的文件和子目录列表,如:

现在,我们就利用文件查找函数,仿制一个dir命令。

#include <stdio.h>
#include <io.h>
#include <time.h>
void print_fileinfo(struct _finddata_t const *fd)
{
struct tm t;
_localtime64_s(&t, &fd->time_create);
printf("%d/%02d/%02d %02d:%02d", t.tm_year + 1900, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_sec);
if (fd->attrib & _A_SUBDIR)
{
printf("%9s", "<DIR>");
printf("%8s", " ");
}
else
{
printf("%9s", " ");
printf("%8ld", fd->size);
}
printf(" %s", fd->name);
printf("\n");
}
int main()
{
int files = 0;
int dirs = 0;
unsigned long bytes = 0;
struct _finddata_t fd;
intptr_t handle;
handle = _findfirst("./*.*", &fd);
if (handle != -1)
{
do
{
if (fd.attrib & _A_SUBDIR)
{
dirs += 1;
}
else
{
files += 1;
bytes += fd.size;
}
print_fileinfo(&fd);
}
while (_findnext(handle, &fd) == 0);
_findclose(handle);
printf("\n");
printf("\t\t%d 个文件 \t %u 字节\n", files, bytes);
printf("\t\t%d 个目录\n", dirs);
}
return 0;
}
在示例程序环境下,_finddata_t的time_create字段是一个__int64类型的数值,故而通过使用_localtime64_s函数,从它构造出一个时间结体变量,然后格式化输出文件的创建日期。在输出<DIR>标志时,"%9s"限定字符串输出最少9个字符宽并右对齐;"%8ld"指出整数的输出宽度为8个字符并右对齐输出。