当前位置: 首页 > news >正文

【C语言加油站】C语言文件随机读写完全指南:fseek、ftell、rewind等五大函数深度解析

随机读写

  • 导读
  • 一、`fseek`
  • 二、`ftell`
    • 2.1 函数介绍
    • 2.2 函数使用
  • 三、`rewind`
    • 3.1 函数介绍
    • 3.2 函数使用
  • 四、`fgetpos`
    • 4.1 函数介绍
    • 4.2 `fpos_t`
  • 五、`fsetpos`
    • 5.1 函数介绍
    • 5.2 函数使用
    • 5.3 函数比较
      • 5.3.1 相同点
      • 5.3.2 不同点
  • 结语

文件操作

导读

大家好,很高兴又和大家见面啦!!!

在上一篇内容中,我们系统学习了C语言文件操作中的顺序读写函数,掌握了如何按部就班地对文件内容进行处理。

无论是字符级的 fgetc/fputc,字符串操作的 fgets/fputs,还是格式化的 fscanf/fprintf,以及二进制的 fread/fwrite,这些函数都遵循着"从头到尾、依次处理"的顺序读写模式。

然而,在实际编程中,我们常常需要更灵活的文件操作方式。今天,我们将进入文件操作的另一个重要领域——随机读写,学习如何精准控制文件中的读写位置。让我们开始探索以下五个核心函数:

  • fseek —— 设置文件中的位置指示器位置
  • ftell —— 获取文件中的位置指示器位置
  • rewind —— 重置文件中的位置指示器位置
  • fgetpos —— 获取文件中的位置指示器位置
  • fsetpos —— 设置文件中的位置指示器位置

一、fseek

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

fseek2
上图给我们介绍了函数的三个参数与返回值,从这两张图片介绍中,我们可以了解该函数的使用方法:

  • 向函数中传入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 函数介绍

ftell
该函数的使用方法为:

  • 向函数中传入一个参数:
    • stream :指向识别流的 FILE 对象指针
  • 函数会从流中获取当前位置指示器的值
    • 成功时,则将当前值返回
    • 失败时,则返回 -1L ,并将 errno 设置为系统的特定正值

2.2 函数使用

该函数的使用我们可以简单的理解为,函数会获取文件中的光标位置:

  • 当文件为二进制文件时,函数的返回值为文件光标从文件开头开始的偏移量
  • 当文件为文本文件时,函数的返回值可能没有实际意义,但是仍可在调用 fseek 时作为函数参数,让 fseek 将光标的位置恢复到当前位置

现在对于 fseekftell 这两个函数我们就能够理解为:

  • 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 函数介绍

fgetpos

该函数的用法如下所示:

  • 向函数中传入两个参数:
    • stream :指向标识流的 FILE 对象指针
    • pos :指向 fpos_t 对象指针
  • 函数会将从 stream 中获取到的当前位置信息填入到 pos
    • 成功时,函数返回 0
    • 发生错误时,errno 会被设置为平台特定的正值,并且函数返回一个非零值

4.2 fpos_t

在函数的用法介绍中,我们看到了一个新的类型:fpos_t 。那么在使用该函数前,我们先来认识一下这个新的类型:

fpos_t

该类型就是专门用于记录文件中的光标位置的类型,该类型的变量中存储的信息,通常是通过 fgetpos 函数进行填充,并且其变量中的信息不能直接读取,只能够在调用 fsetpos 时,作为参数使用。

那也就是说,fgetposfsetpos 这两个函数应该搭配起来使用,那么接下来我们就一起来看看如何 fsetpos 的具体用法;

五、fsetpos

5.1 函数介绍

fsetpos

该函数的用法如下所示:

  • 向函数中传入两个参数:
    • 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 来设置光标位置

下面我们就来测试一下:

函数使用

可以看到,函数 fgetposfsetpos 二者配合起来使用,同样能够达到光标定位与设置的效果。

现在问题来了,这两个函数与前面我们介绍的 ftellfseek 之间有什么不同呢?

5.3 函数比较

5.3.1 相同点

不管是 ftellfseek 的搭配使用,还是 fgetposfsetpos 的搭配使用,他们都是用来完成两件事:

  • 记录光标位置
  • 设置光标位置

5.3.2 不同点

这二者之间不同的地方在于—— 记录光标位置的方式不同

  • ftell 是通过 long int 类型的变量来记录光标的位置
  • fgetpos 则是通过 fpos_t 类型的变量来记录光标位置

因此,这就导致了二者能够处理的文件大小有所区别:

  • 当光标的位置值大于 long int 时,我们再通过 ftell 来记录光标的位置就会出现问题
  • 由于 fgetpos 是通过 fpos_t 来记录,因此,我们不需要考虑光标的位置是否会存在溢出的情况

也就是说,当我们处理大文件时,我们选择使用 fgetposfsetpos 会更加保险一点。

结语

通过今天的学习,我们深入掌握了C语言中实现文件随机读写的五个核心函数。让我们简单回顾一下本篇的重要内容:

函数功能总结

  • fseek:精确定位文件光标,支持三种参考位置(文件头、当前位置、文件尾)

  • ftell:获取当前光标位置,为 fseek 提供定位依据

  • rewind:快速重置光标到文件开头,并清除错误指示器

  • fgetpos:使用fpos_t类型安全记录光标位置

  • fsetpos:与 fgetpos 配合,精准恢复光标位置

关键区别:

  • ftell/fseek 使用 long 类型,适合一般文件操作

  • fgetpos/fsetpos 使用 fpos_t 类型,更适合大文件处理

实践价值

  • 掌握了这些函数,我们就能在文件中自由"穿梭",实现高效的随机访问,为复杂文件操作打下坚实基础。

今天的内容到这里就全部结束了,在下一篇内容中我们将介绍文件操作的错误处理机制,大家记得关注哦!

如果大家觉得这篇博客有帮助,请:
👍 点赞支持 - 您的认可让我更有动力创作
⭐ 关注博主 - 获取更多C语言技术干货
💾 收藏文章 - 方便随时回顾复习
💬 评论留言 - 有任何问题欢迎交流讨论
🔄 转发分享 - 帮助更多需要的朋友

最后感谢各位朋友的支持,咱们下一篇再见!!!

http://www.dtcms.com/a/490846.html

相关文章:

  • C++篇(13)计算器实现
  • 北京网站排行wordpress 搜索小工具栏
  • 阿里云国际代理商:如何实现配置跨区域复制?
  • 全行业智慧零售解决方案|ERP进销存+多端收银+线上商城+分润管理体系
  • 从数据体系到AI落地:数据驱动时代的技术实践与方法论指南(二)
  • MySQL中的常用数据类型详解
  • MySQL 自定义变量(User-Defined Variable)详解与实战
  • 一步步教做音乐网站wordpress开发企业网站
  • 蚌埠网站制作哪里有郑州seo外包顾问
  • 用家里网络做网站设计公司网站的主页怎么做
  • 三维设计可视化编程工具:Dynamo(Autodesk)VS Grasshopper(Rhino)
  • Java JVM “内存(1)”面试清单(含超通俗生活案例与深度理解)
  • LeetCode 刷题【122. 买卖股票的最佳时机 II】
  • Java 黑马程序员学习笔记(进阶篇18)
  • 5-22 WPS JS宏reduce数组的归并迭代应用(实例:提取最大最小值的记录)
  • 郑州营销型网站建设哪家好深圳免费网站排名优化
  • Kubernetes(k8s)版本查看
  • 整型数据与浮点型数据在内存中的存储方法
  • 集合知识点,java学校课
  • 构建AI智能体:六十五、模型智能训练控制:早停机制在深度学习中的应用解析
  • 递归-21.合并两个有序链表-力扣(LeetCode)
  • 中国八大菜系视频课(共800道菜品)
  • 【流式输出】基于Vue实现增量渲染
  • 秦皇岛网站制作费用sns网站社区需求分析文档
  • 【AI论文】面向高效规划与工具使用的流程内智能体系统优化
  • html好看的网站的代码网站加图标
  • conda常用命令pip、venv
  • Visual Studio 2022查看程序变量和堆栈
  • RabbitMQ消息传输中Protostuff序列化数据异常的深度解析与解决方案
  • SSH连接服务器超时?可能原因与解决方案