findfirst 并不是标准 C 语言库(如 ANSI C/C89, C99, C11, C17, C23)的一部分,它是特定于某些操作系统或编译器的函数,最著名的就是 Borland C++ (Turbo C++) 在 DOS 环境下提供的。

尽管如此,由于其简单易用,许多其他非标准的库(例如一些 Windows 的旧版 API 或跨平台库)也提供了类似功能的函数,理解 findfirst 的工作原理对于学习文件操作和了解不同 C 语言的实现方式非常有帮助。
findfirst 的核心概念
findfirst 函数用于在指定的目录中查找第一个符合特定模式的文件或目录,它通常与 findnext 函数配对使用:
findfirst: 开始一次查找,返回第一个匹配项。findnext: 继续查找,返回下一个匹配项。
这个过程会一直持续,直到没有更多匹配项或发生错误。
Borland C++ (Turbo C++) 的实现
这是 findfirst 最经典和最广为人知的实现,它工作在 DOS 环境下。

1 头文件
在使用 findfirst 和 findnext 时,需要包含以下头文件:
#include <dir.h> // 主要的头文件 #include <dos.h> // 有时也需要 #include <stdio.h> // 用于 printf 等标准 I/O
2 核心结构体:ffblk
findfirst 和 findnext 函数通过一个名为 ffblk 的结构体来传递文件信息。
struct ffblk {
char ff_reserved[21]; // 保留,内部使用
char ff_attrib; // 文件属性
int ff_ftime; // 文件时间 (打包的)
int ff_fdate; // 文件日期 (打包的)
long ff_fsize; // 文件大小 (字节)
char ff_name[256]; // 文件名 (包括扩展名)
};
关键成员解释:
ff_attrib: 文件属性,是一个位掩码,常用的值定义在<dos.h>中:FA_RDONLY: 只读文件FA_HIDDEN: 隐藏文件FA_SYSTEM: 系统文件FA_LABEL: 卷标 (不是一个文件)FA_DIREC: 目录FA_ARCH: 存档文件
ff_fsize: 文件大小,以字节为单位。ff_name: 文件名,是一个以 null 结尾的字符串。
3 函数原型
int findfirst(const char *pattern, struct ffblk *ffblk, int attrib); int findnext(struct ffblk *ffblk);
4 参数说明
findfirst 函数参数:

pattern: 查找模式,它是一个字符串,可以包含通配符 (匹配任意数量的字符) 和 (匹配单个字符)。"*.c": 查找所有 C 源文件。"a??.txt": 查找以 'a' 开头,后面跟两个任意字符,扩展名为.txt的文件。"C:\\DATA\\*.dat": 在C:\DATA目录下查找所有.dat文件。
ffblk: 指向ffblk结构体的指针,函数会将找到的第一个文件的信息填充到这个结构体中。attrib: 要查找的文件属性,你可以使用 (位或) 组合多个属性,如果想查找所有文件(包括隐藏和系统文件),通常传入0,如果你想只查找目录,可以传入FA_DIREC。
findnext 函数参数:
ffblk: 指向同一个ffblk结构体的指针。findnext会用下一个匹配文件的信息来更新它。
5 返回值
两个函数都返回一个 int 值,表示操作是否成功:
0: 成功。- 非零值 (
-1): 失败,可以通过doserrno(定义在<dos.h>) 获取具体的错误码。
完整示例代码 (Borland C++ / Turbo C++)
下面的示例演示了如何查找当前目录下所有的 .c 文件,并打印它们的名称和大小。
#include <stdio.h>
#include <dir.h>
#include <string.h>
int main() {
struct ffblk ffblk; // 用于存储文件信息的结构体
int done; // 用于存储 findfirst/next 的返回值
// 开始查找所有 .c 文件
// 第三个参数 0 表示查找所有类型的文件(无论属性)
done = findfirst("*.c", &ffblk, 0);
// findfirst 成功 (返回 0),说明找到了至少一个匹配的文件
if (done == 0) {
printf("找到以下 .c 文件:\n");
printf("----------------------------------------\n");
// 循环处理所有找到的文件
// 当 done == 0 时,表示还有文件可以处理
while (done == 0) {
printf("文件名: %-20s 大小: %ld 字节\n", ffblk.ff_name, ffblk.ff_fsize);
// 尝试查找下一个匹配的文件
done = findnext(&ffblk);
}
printf("----------------------------------------\n");
printf("查找完毕,\n");
} else {
printf("当前目录下没有找到 .c 文件,\n");
}
return 0;
}
现代 C 语言的替代方案:<dirent.h>
在现代操作系统(如 Linux, macOS, Windows 的 POSIX 子系统)上进行 C 语言编程时,应该使用标准化的目录操作头文件 <dirent.h>,它提供了更强大、更可移植的接口。
1 核心结构体:struct dirent
struct dirent {
ino_t d_ino; // inode 编号 (Linux/Unix 特有)
off_t d_off; // 在目录文件中的偏移量 (Linux/Unix 特有)
unsigned short d_reclen; // 目录记录的长度
unsigned char d_type; // 文件类型 (DT_REG, DT_DIR, DT_LNK 等)
char d_name[256]; // 文件名,以 null
};
2 核心函数
DIR *opendir(const char *name): 打开一个目录流,返回一个指向DIR结构体的指针。struct dirent *readdir(DIR *dirp): 从打开的目录流中读取下一个目录项。int closedir(DIR *dirp): 关闭目录流。
3 <dirent.h> 示例
下面的示例实现了与上面 findfirst 示例完全相同的功能,但使用了标准 <dirent.h> 接口,可以在 Linux 和 macOS 上编译运行。
#include <stdio.h>
#include <string.h>
#include <dirent.h> // 标准目录操作头文件
int main() {
DIR *dp; // 目录指针
struct dirent *entry; // 目录项指针
char *dirname = "."; // 当前目录
const char *ext = ".c"; // 要查找的文件扩展名
// 打开目录
dp = opendir(dirname);
if (dp == NULL) {
perror("无法打开目录");
return 1;
}
printf("找到以下 %s 文件:\n", ext);
printf("----------------------------------------\n");
// 读取目录中的每一个条目
while ((entry = readdir(dp)) != NULL) {
// 检查文件名是否以 .c
if (strstr(entry->d_name, ext) != NULL) {
printf("文件名: %-20s\n", entry->d_name);
// 注意:readdir 本身不提供文件大小信息,
// 需要使用 stat() 函数来获取。
}
}
printf("----------------------------------------\n");
printf("查找完毕,\n");
// 关闭目录
closedir(dp);
return 0;
}
Windows 平台的替代方案:<io.h>
在 Windows 平台上进行原生 C/C++ 开发时,可以使用另一个非标准但非常普遍的头文件 <io.h>,它提供了类似 findfirst 的功能,并且是 Windows 推荐的旧式文件查找方式。
1 核心结构体:_finddata_t
struct _finddata_t {
unsigned attrib; // 文件属性
__time64_t time_create; // 创建时间
__time64_t time_access; // 最后访问时间
__time64_t time_write; // 最后写入时间
_fsize_t size; // 文件大小
char name[260]; // 文件名
};
2 核心函数
long _findfirst(const char *filespec, struct _finddata_t *fileinfo): 查找第一个匹配的文件。int _findnext(long handle, struct _finddata_t *fileinfo): 查找下一个匹配的文件。int _findclose(long handle): 关闭查找句柄。
3 <io.h> 示例
#include <stdio.h>
#include <io.h> // Windows 文件查找头文件
#include <stdlib.h> // 用于 _MAX_PATH
int main() {
struct _finddata_t c_file;
long hFile;
int result = 1;
// 查找第一个 .c 文件
// _MAX_PATH 是一个定义好的最大路径长度常量
if ((hFile = _findfirst("*.c", &c_file)) == -1L) {
printf("找不到任何 .c 文件\n");
} else {
printf("找到以下 .c 文件:\n");
printf("----------------------------------------\n");
// 循环打印所有找到的文件
while (result == 0) {
printf("文件名: %-20s 大小: %d 字节\n", c_file.name, c_file.size);
result = _findnext(hFile, &c_file);
}
printf("----------------------------------------\n");
printf("查找完毕,\n");
// 关闭查找句柄
_findclose(hFile);
}
return 0;
}
总结与对比
| 特性 | findfirst (Borland C) |
<dirent.h> (标准 POSIX) |
<io.h> (Windows) |
|---|---|---|---|
| 平台 | DOS (过时) | Linux, macOS, Windows (WSL/Cygwin) | Windows (原生) |
| 标准性 | 非标准 | 标准 | 非标准 (但广泛使用) |
| 头文件 | #include <dir.h> |
#include <dirent.h> |
#include <io.h> |
| 核心结构 | struct ffblk |
struct dirent |
struct _finddata_t |
| 主要函数 | findfirst, findnext |
opendir, readdir, closedir |
_findfirst, _findnext, _findclose |
| 优点 | 非常简单直观 | 跨平台,功能强大,现代标准 | 在 Windows 上高效,功能丰富 |
| 缺点 | 仅限 DOS,已过时 | API 稍复杂 (opendir/readdir) | 仅限 Windows,可移植性差 |
- 如果你正在学习历史代码或在 DOS 环境下编程,你需要了解
findfirst。 - 如果你正在为现代、跨平台的 Unix-like 系统(Linux, macOS)编写代码,必须使用
<dirent.h>。 - 如果你正在编写 Windows 原生应用程序,并且不关心跨平台性,
<io.h>是一个非常好的选择,比findfirst更现代、更强大。
