核心概述
seekdir 是 C 标准库中的一个函数,主要用于在已打开的目录流(由 opendir 函数创建)中,将读取位置(即“目录指针”)移动到指定的位置。

(图片来源网络,侵删)
它就像文件操作中的 fseek 函数,只不过操作对象是目录而不是文件。
函数原型
seekdir 函数在 <dirent.h> 头文件中声明,其原型如下:
#include <dirent.h> void seekdir(DIR *dirp, long loc);
参数详解
-
dirp:- 类型为
DIR *,这是一个指向目录流的指针。 - 它必须是通过
opendir()函数成功打开一个目录后返回的指针。 - 如果传入一个无效的、未初始化的或已经关闭的
DIR指针,行为是未定义的(Undefined Behavior),可能会导致程序崩溃。
- 类型为
-
loc:
(图片来源网络,侵删)- 类型为
long,它代表了在目录流中新的偏移位置。 - 这个位置通常不是通过用户手动计算的,而是由
telldir()函数获取的。telldir()会返回当前目录指针的位置值。 - 你不能随意传一个数字进去,除非你确切地知道这个数字代表哪个目录项。
- 类型为
功能说明
seekdir 函数的作用是修改 dirp 所指向的目录流的内部读取指针,调用 seekdir 之后,后续对该目录流进行 readdir() 操作时,将会从 seekdir 指定的位置开始读取目录项。
重要特性:
- 位置无效化:在调用
seekdir之后,之前由telldir()获取的任何位置值都会失效,你不能在调用seekdir后再使用旧的loc值。 - 与
readdir的交互:seekdir会影响readdir的读取顺序,如果你在调用seekdir后又调用了rewinddir(),seekdir设置的位置也会失效。
返回值
seekdir 函数没有返回值(即返回类型为 void),它总是默默地执行操作,不报告成功或失败,调用者需要确保传入的 dirp 指针是有效的。
使用示例
seekdir 通常和 telldir、readdir、opendir、closedir 配合使用,一个典型的应用场景是:读取目录,发现某个文件后,回到目录开头重新查找另一个特定条件的文件。
下面是一个完整的示例程序:
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <directory>\n", argv[0]);
return 1;
}
DIR *dirp = opendir(argv[1]);
if (dirp == NULL) {
perror("opendir failed");
return 1;
}
struct dirent *entry;
long first_loc = -1; // 用于记录第一个找到的 ".txt" 文件的位置
printf("--- First pass: looking for .txt files ---\n");
// 第一次遍历目录
while ((entry = readdir(dirp)) != NULL) {
// 查找第一个以 ".txt" 结尾的文件
if (strstr(entry->d_name, ".txt") != NULL) {
printf("Found first .txt file: %s\n", entry->d_name);
first_loc = telldir(dirp); // 记录当前指针位置
break; // 找到后立即退出循环
}
}
// 如果找到了 .txt 文件,进行第二次遍历
if (first_loc != -1) {
printf("\n--- Second pass: starting from the beginning ---\n");
seekdir(dirp, 0); // 将指针重置到目录开头
// 注意:这里用 seekdir(dirp, 0) 更通用,
// 也可以用 rewinddir(dirp)。
// 第二次遍历目录
while ((entry = readdir(dirp)) != NULL) {
// 这次我们查找所有以 "log" 开头的文件
if (strncmp(entry->d_name, "log", 3) == 0) {
printf("Found a log file: %s\n", entry->d_name);
}
}
}
// 关闭目录流
closedir(dirp);
return 0;
}
如何编译和运行:
假设你有一个名为 my_test_dir 的目录,里面包含以下文件:
data.txtlog1.txtlog2.txtimage.jpgnotes.txt
# 1. 创建测试目录和文件 mkdir my_test_dir touch my_test_dir/data.txt my_test_dir/log1.txt my_test_dir/log2.txt my_test_dir/image.jpg my_test_dir/notes.txt # 2. 编译代码 gcc seekdir_example.c -o seekdir_example # 3. 运行程序 ./seekdir_example my_test_dir
预期输出:
--- First pass: looking for .txt files ---
Found first .txt file: data.txt
--- Second pass: starting from the beginning ---
Found a log file: log1.txt
Found a log file: log2.txt
输出解释:
- 第一次遍历:程序读取
my_test_dir,第一个找到的.txt文件是data.txt。readdir的指针已经指向了log1.txt的位置,程序用telldir记录了这个位置(虽然我们没有打印出来)。 - 第二次遍历:程序调用
seekdir(dirp, 0)将目录指针重置到开头,然后再次调用readdir,这次它从data.txt开始读,但我们的条件是查找以 "log" 开头的文件,所以最终找到了log1.txt和log2.txt。
重要注意事项
telldir和seekdir的可移植性:POSIX 标准保证telldir和seekdir可以一起工作,但标准 C (ISO C) 并不强制要求,在绝大多数现代操作系统(如 Linux, macOS, Windows with MinGW/Cygwin)上,它们都是可用的且行为一致,如果你需要编写高度可移植的代码,应查阅目标平台的文档。telldir返回值的含义:telldir返回的long值是一个不透明的(opaque)句柄,它只对seekdir有意义,你不应该尝试对这个值进行数学运算或假设它代表某个具体的字节偏移量。rewinddirvsseekdir(dirp, 0):rewinddir(dirp)函数的作用是将目录指针重置到开头,从功能上讲,seekdir(dirp, 0)也能达到同样的效果。rewinddir是专门为这个目的设计的,其意图更加清晰,可读性更好,在代码中,如果你想重置到开头,优先使用rewinddir。- 性能考虑:频繁地在目录中前后移动指针可能会比从头开始重新遍历整个目录要慢,具体取决于底层文件系统的实现,对于大多数应用场景,简单的线性遍历已经足够高效。
| 函数 | 功能 | 相关函数 |
|---|---|---|
seekdir |
将目录流的读取指针移动到由 telldir 指定的位置 |
telldir, readdir, rewinddir |
telldir |
获取当前目录流的读取指针位置 | seekdir |
readdir |
读取目录流中的下一个目录项 | opendir, closedir |
rewinddir |
将目录流的读取指针重置到开头 | seekdir(dirp, 0) |
opendir |
打开一个目录,返回 DIR* 指针 |
readdir, closedir |
closedir |
关闭一个 DIR* 目录流 |
opendir |
seekdir 是一个功能强大但使用场景相对特定的函数,主要用于需要在单次 opendir/closedir 会话中对目录进行多次、非顺序访问的复杂程序中,对于简单的遍历需求,直接循环调用 readdir 即可。
