核心概念
fseek 是 C 标准库中的一个函数,用于移动文件指针到指定的位置,文件指针是一个内部变量,它指向了文件中当前读/写操作将要发生的位置。

(图片来源网络,侵删)
它的原型定义在 <stdio.h> 头文件中:
int fseek(FILE *stream, long int offset, int origin);
参数详解
-
FILE *stream- 这是指向
FILE对象的指针,也就是你要操作的文件流,这个指针通常是通过fopen()函数调用成功后返回的。 FILE *fp = fopen("my_file.txt", "r");,这里的fpfseek的第一个参数。
- 这是指向
-
long int offset- 这是一个偏移量,它是一个有符号的长整型,它表示从“起始点”(由第三个参数决定)移动多少个字节。
offset是正数,表示向文件末尾方向移动。offset是负数,表示向文件开头方向移动。offset是 0,表示不移动,但配合不同的origin仍有意义。
-
int origin
(图片来源网络,侵删)- 这是起始点,它决定了
offset是从哪里开始计算的,它必须是一个宏,有三个可选值:SEEK_SETSEEK_CURSEEK_END
- 这是起始点,它决定了
SEEK_SET 及其他宏详解
这三个宏是 fseek 函数的灵魂,它们定义了计算偏移量的“零点”。
SEEK_SET
- 含义:文件开头 (Set the file position to the beginning of the file)。
- 行为:将文件指针移动到距离文件开头
offset个字节的位置。 - 公式:
新位置 = 文件开头 + offset - 关键点:
offset必须是一个非负数(0 或正数),你不能用SEEK_SET来移动到文件开头之前的位置,这是未定义行为。
示例:
假设文件 data.txt 内容为 "Hello, World!" (共13个字符)。
FILE *fp = fopen("data.txt", "r");
if (fp) {
// 将文件指针移动到文件开头 + 7 的位置
// 也就是指向 'W'
fseek(fp, 7, SEEK_SET);
// 现在可以读取 'W' 及其后面的内容
char ch = fgetc(fp); // ch 将会是 'W'
printf("Character at offset 7: %c\n", ch); // 输出: Character at offset 7: W
fclose(fp);
}
SEEK_CUR
- 含义:当前位置 (Set the file position to the current position)。
- 行为:从文件指针的当前位置开始,移动
offset个字节。 - 公式:
新位置 = 当前位置 + offset - 关键点:
offset可以为正(向后移动)、负(向前移动)或零。
示例:
继续使用文件 "Hello, World!"。
FILE *fp = fopen("data.txt", "r");
if (fp) {
// 1. 先读取前6个字符 "Hello,"
for (int i = 0; i < 6; i++) {
fgetc(fp);
}
// 文件指针指向 ' ' (空格,第6个字符之后的位置)
// 2. 从当前位置向前移动 2 个字节
// 即从 ' ' 移动到 'o'
fseek(fp, -2, SEEK_CUR);
// 3. 读取字符,应该是 'o'
char ch = fgetc(fp); // ch 将会是 'o'
printf("Character 2 bytes back from current: %c\n", ch); // 输出: Character 2 bytes back from current: o
fclose(fp);
}
SEEK_END
- 含义:文件末尾 (Set the file position to the end of the file)。
- 行为:将文件指针移动到距离文件末尾
offset个字节的位置。 - 公式:
新位置 = 文件末尾 + offset - 关键点:
offset通常为 0 或负数,将offset设为 0 是将指针移动到文件末尾的常用方法,使用正数offset会导致移动到文件末尾之后,这也是未定义行为。
示例:
继续使用文件 "Hello, World!"。

(图片来源网络,侵删)
FILE *fp = fopen("data.txt", "r");
if (fp) {
// 将文件指针移动到文件末尾
fseek(fp, 0, SEEK_END);
// 文件指针已经指向了文件的最后一个字节之后
// 我们可以用 ftell() 获取当前指针位置,它等于文件大小
long file_size = ftell(fp);
printf("File size: %ld bytes\n", file_size); // 输出: File size: 13 bytes
// 将指针从末尾向前移动 6 个字节,指向 'W'
fseek(fp, -6, SEEK_END);
char ch = fgetc(fp); // ch 将会是 'W'
printf("Character 6 bytes from the end: %c\n", ch); // 输出: Character 6 bytes from the end: W
fclose(fp);
}
fseek 的返回值
fseek 函数执行成功时返回 0,失败时返回一个非零值(通常是 -1),在使用 fseek 后,最好检查其返回值以确保操作成功。
if (fseek(fp, 100, SEEK_SET) != 0) {
perror("Failed to seek in file");
// 处理错误
}
典型应用场景
-
获取文件大小 这是最常见的用法之一。
long get_file_size(const char *filename) { FILE *fp = fopen(filename, "rb"); // "rb" 表示二进制读取,避免平台换行符问题 if (fp == NULL) { return -1; // 打开失败 } fseek(fp, 0, SEEK_END); long size = ftell(fp); fclose(fp); return size; } -
随机访问文件 比如一个数据库文件,每条记录固定长度,你可以快速定位到某条记录。
// 假设每条记录 100 字节,要读取第 5 条记录 fseek(fp, (5 - 1) * 100, SEEK_SET); // 现在指针就在第5条记录的开头,可以读取了
-
重置文件指针到开头 当你读取完一个文件后,如果想从头再读一遍,而不是关闭再重新打开文件,可以使用:
rewind(fp); // rewind(fp) 等价于 fseek(fp, 0, SEEK_SET); // 或者 fseek(fp, 0, SEEK_SET);
-
清空输入缓冲区 在某些交互式程序中,如果用户输入了多余的数据(比如你只读一个字符,但他输入了一整行),可以用
fseek来跳过这些未读取的字符。char c; scanf("%c", &c); // 用户输入 'a' 和回车 // stdin 的缓冲区里还有 '\n' fseek(stdin, 0, SEEK_CUR); // 通常没什么用 // 更好的方法是直接读取并丢弃剩余字符 // while (getchar() != '\n'); // 这是一个更常见的做法
重要注意事项
-
文本模式 vs. 二进制模式
- 在二进制模式 (
"rb","wb") 下,fseek的行为是精确的,offset就是字节数。 - 在文本模式 (
"r","w") 下,情况会变得复杂,因为不同平台对换行符的处理不同(Windows 是\r\n,Linux/macOS 是\n),在文本模式下进行非零的fseek操作是不推荐的,可能导致定位错误,如果需要进行精确的文件指针操作,强烈建议始终以二进制模式打开文件。
- 在二进制模式 (
-
流的状态
- 在调用
fseek之前,如果上一次操作是fwrite或fputc(写操作),必须先调用fflush()或fseek来确保所有缓冲数据都已写入文件,否则fseek的行为是未定义的。 - 在调用
fseek之后,流的读写模式可能会改变(从读模式切换到写模式,反之亦然),为了安全起见,最好在fseek之后进行一次明确的读或写操作。
- 在调用
-
错误处理
- 永远不要忽略
fseek的返回值,如果文件是只读的,而你试图用SEEK_END加上一个正数去移动,fseek就会失败。
- 永远不要忽略
总结表格
| 宏 (Macro) | 含义 (Meaning) | 起始点 (Origin) | offset 典型值 |
示例 (fseek(fp, offset, origin)) |
|---|---|---|---|---|
SEEK_SET |
文件开头 (Beginning of file) | 文件的第 0 个字节 | 非负数 (>= 0) | fseek(fp, 10, SEEK_SET); -> 移动到第 10 个字节。 |
SEEK_CUR |
当前位置 (Current position) | 指针所在的位置 | 正数、负数或零 | fseek(fp, 5, SEEK_CUR); -> 向后移动 5 字节。fseek(fp, -3, SEEK_CUR); -> 向前移动 3 字节。 |
SEEK_END |
文件末尾 (End of file) | 文件最后一个字节之后 | 零或负数 (<= 0) | fseek(fp, 0, SEEK_END); -> 移动到文件末尾。fseek(fp, -5, SEEK_END); -> 从末尾向前移动 5 字节。 |
