导读
无论是字符级的 fgetc/fputc,字符串操作的 fgets/fputs,还是格式化的 fscanf/fprintf,以及二进制的 fread/fwrite,这些函数都遵循着"从头到尾、依次处理"的顺序读写模式。
然而,在实际编程中,我们常常需要更灵活的文件操作方式。今天,我们将进入文件操作的另一个重要领域——随机读写,学习如何精准控制文件中的读写位置。让我们开始探索以下五个核心函数:
fseek—— 设置文件中的位置指示器位置ftell—— 获取文件中的位置指示器位置rewind—— 重置文件中的位置指示器位置fgetpos—— 获取文件中的位置指示器位置fsetpos—— 设置文件中的位置指示器位置
一、fseek

上图给出了函数用法的介绍,我们可以看到该函数不仅能够对文本文件使用,还可以对二进制文件使用;

上图给我们介绍了函数的三个参数与返回值,从这两张图片介绍中,我们可以了解该函数的使用方法:
- 向函数中传入3个参数
stream:指向识别流的file对象指针offset:文件中光标的位置- 二进制文件中:从
origin的参考值中偏移的字节数 - 文本文件中:0 或者由
ftell函数获取的返回值
- 二进制文件中:从
origin:为offset参考的位置seek_set: 文件开头seek_cur:文件指针的当前位置seek_end:文件末尾
- 函数会将与流关联的文件内部的光标设置为一个新位置:
- 二进制文件:新位置由
origin的参考位置的偏移量offset定义 - 文本文件:新位置为
0或者由先前调用ftell函数时的返回值,并且origin的参考值必须为seek_set,即文件开头; - 当函数在这些参考值之外调用其他值,是否支持还取决于特定的系统和库的实现。即若当前系统支持,那此时的使用方法无法移植。
- 二进制文件:新位置由
- 函数在完成设置后,会给出相应的返回值:
- 设置成功,则函数返回
0 - 设置失败,则函数返回非零值
- 如果发生读取或写入错误,则函数会设置错误指示器(
ferror)
- 设置成功,则函数返回
从用法中我们可以看到,该函数的使用与 ftell 函数是分不开的,因此我们下面直接来看一下 ftell 函数应该如何使用;
二、ftell
2.1 函数介绍

该函数的使用方法为:
- 向函数中传入一个参数:
stream:指向识别流的file对象指针
- 函数会从流中获取当前位置指示器的值
- 成功时,则将当前值返回
- 失败时,则返回
-1l,并将errno设置为系统的特定正值
2.2 函数使用
该函数的使用我们可以简单的理解为,函数会获取文件中的光标位置:
- 当文件为二进制文件时,函数的返回值为文件光标从文件开头开始的偏移量
- 当文件为文本文件时,函数的返回值可能没有实际意义,但是仍可在调用
fseek时作为函数参数,让fseek将光标的位置恢复到当前位置
现在对于 fseek 和 ftell 这两个函数我们就能够理解为:
ftell用于获取文件中的光标位置fseek用于设置文件中的光标位置
接下来我们就可以尝试着使用这两个函数了:
void test1() {
file* pf = fopen("c:\\users\\liqian\\desktop\\data.txt", "r+");
if (pf == null) {
perror("fopen");
return;
}
// 通过 ftell 获取文件中的光标当前位置
long int index = ftell(pf);
// 检查返回值
if (index == -1l) {
perror("ftell");
fclose(pf);
pf = null;
return;
}
// 通过 fgetc 读取当前位置的文本信息
int ch = fgetc(pf);
while (ch != eof) {
printf("ch = %c\n", ch);
ch = fgetc(pf);
}
// 通过 fseek 设置文件中的光标位置
int set = fseek(pf, index, seek_set);
// 由于我们打开的为文本文件,因此 origin 的值必须为文件开头(seek_set)
if (set != 0) {
perror("fseek");
fclose(pf);
pf = null;
return;
}
// 再一次读取当前位置的元素
ch = fgetc(pf);
printf("ch = %c\n", ch);
fclose(pf);
pf = null;
}
这里的测试代码逻辑比较简单:
- 先通过
ftell记录文件当前的光标起始位置 - 通过
fgetc进行内容读取,直到光标移动到文件末尾 - 之后通过
fseek重新设置光标的位置 - 最后再一次通过
fgetc来读取当前位置的内容
下面我们就来运行以下该测试代码:

从测试结果中可以看到,文件刚打开时,光标的位置为文件开头,当我们通过 ftell 记录了此时的位置后,我们通过 fgetc 移动了光标,不管光标最后在哪里,我们都可以通过 fseek 函数将光标设置到最初我们记录的位置;
那可能有朋友会问,如果我们在记录光标的起始位置时,光标并不在文件开头,这时我们需要将光标重置到文件开头,应该怎么办呢?
此时有两种办法:
- 通过将
offset参数值置为0,即int set = fseek(pf, 0, seek_set); - 通过
rewind函数重置光标位置
接下来,我们就来看看 rwind 函数的具体用法;
三、rewind
3.1 函数介绍

该函数的用法如下:
- 向函数中传入一个参数:
stream:指向标识流的file对象指针
- 函数会清除流中的文件末尾内部指示器和错误内部指示器,并将文件内部的位置指示器设置为文件开头
3.2 函数使用
该函数简单的理解就是用于重置文件内部的光标位置。接下来我们就来对其进行测试:
void test2() {
file* pf = fopen("c:\\users\\liqian\\desktop\\data.txt", "r+");
if (pf == null) {
perror("fopen");
return;
}
// 通过 fgetc 先读取一定的文本信息
for (int i = 0; i < 3; i++) {
int ch = fgetc(pf);
printf("ch = %c ", ch);
}
printf("\n");
// 通过 ftell 获取文件中的光标当前位置
long int index = ftell(pf);
// 检查返回值
if (index == -1l) {
perror("ftell");
fclose(pf);
pf = null;
return;
}
// 通过 fgetc 读取当前位置的文本信息
int ch = fgetc(pf);
while (ch != eof) {
printf("ch = %c ", ch);
ch = fgetc(pf);
}
printf("\n");
// 通过 fseek 设置文件中的光标位置
int set = fseek(pf, index, seek_set);
// 由于我们打开的为文本文件,因此 origin 的值必须为文件开头(seek_set)
if (set != 0) {
perror("fseek");
fclose(pf);
pf = null;
return;
}
// 再一次读取当前位置的元素
ch = fgetc(pf);
printf("ch = %c\n", ch);
// 通过 rewind 重置光标位置
rewind(pf);
// 再一次读取当前位置的元素
ch = fgetc(pf);
printf("ch = %c\n", ch);
fclose(pf);
pf = null;
}
我们此时的测试逻辑很简单:
- 文件在打开时,光标会位于文件开头
- 首先,我们通过
fgetc读取数据来移动光标 - 完成写入后,通过
ftell来记录光标此时的位置 - 接下来,我们继续通过
fgetc读取数据,进一步移动光标 - 之后,我们再通过
fseek来恢复光标的位置 - 最后,我们再通过
rewind来重置光标的位置
下面我们就来对其测试一下:

可以看到,当我们使用 rewind 后,光标的位置就从我们最开始记录的 l 处重置为了文件开头 h 处。
因此当我们在对文件进行操作时,如果我们需要重置光标的位置,我们就可以通过 rewind 实现。
四、fgetpos
4.1 函数介绍

该函数的用法如下所示:
- 向函数中传入两个参数:
stream:指向标识流的file对象指针pos:指向fpos_t对象指针
- 函数会将从
stream中获取到的当前位置信息填入到pos中- 成功时,函数返回
0 - 发生错误时,
errno会被设置为平台特定的正值,并且函数返回一个非零值
- 成功时,函数返回
4.2 fpos_t
在函数的用法介绍中,我们看到了一个新的类型:fpos_t 。那么在使用该函数前,我们先来认识一下这个新的类型:

该类型就是专门用于记录文件中的光标位置的类型,该类型的变量中存储的信息,通常是通过 fgetpos 函数进行填充,并且其变量中的信息不能直接读取,只能够在调用 fsetpos 时,作为参数使用。
那也就是说,fgetpos 与 fsetpos 这两个函数应该搭配起来使用,那么接下来我们就一起来看看如何 fsetpos 的具体用法;
五、fsetpos
5.1 函数介绍

该函数的用法如下所示:
- 向函数中传入两个参数:
stream:指向标识流的file对象指针pos:指向fpos_t对象指针
- 函数会将
stream的当前位置恢复到pos中- 成功时,函数返回
0 - 发生错误时,
errno会被设置为平台特定的正值,并且函数返回一个非零值
- 成功时,函数返回
5.2 函数使用
该函数的用法我们可以简单的理解为,将文件中的光标位置恢复到 pos 中存储的位置。
这里需要注意的是,pos_t 的对象中存储的信息只能通过 fgetpos 获取,我们无法直接对其进行赋值操作!!!
接下来我们就来尝试着使用一下这两个函数:
void test3() {
file* pf = fopen("c:\\users\\liqian\\desktop\\data.txt", "r");
if (pf == null) {
perror("fopen");
return;
}
// 通过 fgetc 读取数据来移动光标
for (int i = 0; i < 3; i++) {
int ch = fgetc(pf);
printf("ch = %c\n", ch);
}
printf("\n");
// 记录光标位置变量
fpos_t pos;
// 通过 fgetpos 获取当前光标位置
int get = fgetpos(pf, &pos);
// 通过 fgetc 移动光标
int ch = fgetc(pf);
while (ch != '!') {
printf("ch = %c\n", ch);
ch = fgetc(pf);
}
printf("\nch = %c\n", ch);
// 通过 fsetpos 来恢复光标位置
int set = fsetpos(pf, &pos);
ch = fgetc(pf);
printf("\nch = %c\n", ch);
fclose(pf);
pf = null;
}
这一次我们测试的逻辑与前面的逻辑一致:
- 先通过
fgetc来移动光标 - 再通过
fgetpos来获取光标位置信息 - 之后再一次通过
fgetc来移动光标 - 最后通过
fsetpos来设置光标位置
下面我们就来测试一下:

可以看到,函数 fgetpos 与 fsetpos 二者配合起来使用,同样能够达到光标定位与设置的效果。
现在问题来了,这两个函数与前面我们介绍的 ftell 和 fseek 之间有什么不同呢?
5.3 函数比较
5.3.1 相同点
不管是 ftell 与 fseek 的搭配使用,还是 fgetpos 与 fsetpos 的搭配使用,他们都是用来完成两件事:
- 记录光标位置
- 设置光标位置
5.3.2 不同点
这二者之间不同的地方在于—— 记录光标位置的方式不同:
ftell是通过long int类型的变量来记录光标的位置fgetpos则是通过fpos_t类型的变量来记录光标位置
因此,这就导致了二者能够处理的文件大小有所区别:
- 当光标的位置值大于
long int时,我们再通过ftell来记录光标的位置就会出现问题 - 由于
fgetpos是通过fpos_t来记录,因此,我们不需要考虑光标的位置是否会存在溢出的情况
也就是说,当我们处理大文件时,我们选择使用 fgetpos 和 fsetpos 会更加保险一点。
结语
通过今天的学习,我们深入掌握了c语言中实现文件随机读写的五个核心函数。让我们简单回顾一下本篇的重要内容:
函数功能总结:
fseek:精确定位文件光标,支持三种参考位置(文件头、当前位置、文件尾)ftell:获取当前光标位置,为fseek提供定位依据rewind:快速重置光标到文件开头,并清除错误指示器fgetpos:使用fpos_t类型安全记录光标位置fsetpos:与fgetpos配合,精准恢复光标位置
关键区别:
ftell/fseek使用long类型,适合一般文件操作fgetpos/fsetpos使用fpos_t类型,更适合大文件处理
实践价值:
掌握了这些函数,我们就能在文件中自由"穿梭",实现高效的随机访问,为复杂文件操作打下坚实基础。
以上就是c语言文件随机读写的完全指南的详细内容,更多关于c语言文件随机读写的资料请关注代码网其它相关文章!
发表评论