流动的代码:文件流畅读写的艺术(三)

简介:

函数对比

scanf,fscanf,sscanf

scanf、fscanf 和 sscanf 是 C 语言中用于输入操作的函数,特别是用于格式化输入。它们属于标准输入/输出库,用于按照指定格式从不同来源读取数据。

以下是它们的基本详情和区别:


scanf ( ):


用途:它从标准输入流(stdin)读取输入,通常是键盘。

格式:int scanf(const char *format, …);

目的:用于根据提供的格式说明符从标准输入读取各种数据类型。

示例:读取一个整数和一个字符。


int i;
char c;
scanf("%d %c", &i, &c);
1
2
3
fscanf ( ):


用途:它从文件流读取输入,不仅限于 stdin。

格式:int fscanf(FILE *stream, const char *format, …);

目的:它类似于 scanf,但可用于任何使用 fopen 函数打开的文件或任何预定义的文件流。这允许从文件或其他输入流读取格式化输入。

示例:从文件中读取一个整数。


FILE *fp;
int n;
fp = fopen("file.txt", "r");
if(fp != NULL) {
    fscanf(fp, "%d", &n);
    fclose(fp);
}


sscanf 函数

sscanf 函数用于从字符串中按指定格式读取数据,这对于解析字符串中的特定数据非常有用


int sscanf(const char *str, const char *format, ...);

1

str:要读取数据的源字符串。

format:格式字符串,指定了希望从源字符串中读取数据的类型和格式。

‘…’:额外的参数,用于存储从源字符串中按照格式字符串读取的数据。

返回值:返回成功读取的数据项的数量。如果在读取任何数据之前遇到错误或到达字符串的结尾,则返回EOF


假设你有一个包含整数和浮点数的字符串,你想从中提取这些数值:

1. #include 
2. 
3. int main() {
4.     char *str = "100 3.14";
5.     int i;
6.     float f;
7. 
8.     int result = sscanf(str, "%d %f", &i, &f);
9. 
10.     if (result == 2) {
11.         printf("整数:%d\n", i);
12.         printf("浮点数:%f\n", f);
13.     } else {
14.         printf("格式化读取失败\n");
15.     }
16. 
17.     return 0;
18. }



在这个例子中,sscanf 会尝试从字符串 “100 3.14” 中读取一个整数和一个浮点数。如果成功,它会返回读取的项数(在这个例子中是2),并且变量 i 和 f 将分别被赋值为100和3.14。

注意事项


安全性:与其他格式化输入函数一样,使用 sscanf 时需注意安全性,特别是对字符串的长度和格式的处理,以避免溢出等问题。

错误处理:检查 sscanf 的返回值来确认成功读取的数据项数量,这对于验证和错误处理很重要。

使用场景:sscanf 特别适用于从已经存在的字符串中提取数据,例如解析来自文件、网络或用户输入的数据。

printf , fprintf , sprintf

printf 函数


int printf(const char *format, ...);


用途:将格式化的输出发送到标准输出,通常是屏幕(控制台)。


format:格式字符串,指定了输出的格式。

‘…’:可变参数列表,包含要输出的数据。

示例:向控制台打印整数和字符串。


int num = 10;
char *arr = "Hello, world!";
printf("Number: %d, arr: %s\n", num, arr);


fprintf 函数


int fprintf(FILE *stream, const char *format, ...);

1

用途:将格式化的输出发送到指定的文件流中,可以是任何 FILE 类型的流,包括标准输出(stdout)和标准错误(stderr)。


示例:向文件写入格式化文本。

1. FILE *fp = fopen("output.txt", "w");
2. if (fp != NULL) {
3.     fprintf(fp, "Number: %d\n", num);
4.     fclose(fp);
5. }

sprintf函数

sprintf 函数用于将格式化的数据写入字符串。它是标准输入输出库中的一个重要函数,特别适用于创建格式化字符串


int sprintf(char *str, const char *format, ...);

1

返回值:返回写入到目标字符串的字符数,不包括终结的空字符(‘\0’)。如果发生错误,则可能返回负值。


假设您想将一个整数和一个浮点数格式化为一个字符串:


1. #include 
2. 
3. int main() {
4.     int num = 25;
5.     float pi = 3.14159;
6.     char buffer[50];
7. 
8.     sprintf(buffer, "%d, %f", num, pi);
9. 
10.     printf("格式化后的字符串:%s\n", buffer);
11. 
12.     return 0;
13. }

在这个例子中,sprintf 将整数 num 和浮点数 pi 按指定的格式写入字符串 buffer。之后,可以使用 printf 打印这个字符串,或者以其他方式使用它。


文件的随机读写

顺序读写数据是按照顺序一个接一个地读取或写入的,通常从文件的开始位置开始,然后逐步向后移动,直到文件结束。

而随机读写允许直接跳转到文件中的任何位置进行读取或写入。不必遵循特定的顺序,可以访问文件的任何部分


fseek函数

fseek 函数用于在文件中移动文件指针到指定位置,从而实现文件的随机访问


int fseek(FILE *stream, long offset, int origin);


offset:相对于 origin 参数所指定位置的偏移量,以字节为单位。

origin:起始位置,它可以是 SEEK_SET(文件开头)、==SEEK_CUR(当前位置)==或 SEEK_END(文件末尾)


先举一个例子:


int main()
{
  FILE* pf = fopen("test2.txt", "r");
  if (pf == NULL)
  {
  perror("fopen");
  return 1;
  }
  int ch = fgetc(pf);
  printf("%c\n", ch);
  ch = fgetc(pf);
  printf("%c\n", ch);
  ch = fgetc(pf);
  printf("%c\n", ch);
  fclose(pf);
  pf = NULL;
  return 0;
}


在test2.txt中我们放入abcdefgh,打印结果


这里,移动文件指针按照顺序移动,那么如果我想让指针重新指向a呢?这里就得使用fseek函数:


fseek(pf,-3, SEEK_CUR);


这里从当前位置移动,-3即为向文件开头方向移动.

打印结果:



fseek(pf,0, SEEK_SET);


这种写法是从起始位置偏移0个字符,所以还是起始位置



ftell函数

若现在不知道偏移量是多少,就可以使用ftell函数;


long ftell(FILE *stream);


比如上面的例子:

int main()
{
  FILE* pf = fopen("test2.txt", "r");
  if (pf == NULL)
  {
  perror("fopen");
  return 1;
  }
  int ch = fgetc(pf);
  printf("%c\n", ch);
  ch = fgetc(pf);
  printf("%c\n", ch);
  ch = fgetc(pf);
  printf("%c\n", ch);
  int n = ftell(pf);
  printf("%d\n", n);
  fseek(pf,0, SEEK_SET);
  ch = fgetc(pf);
  printf("%c\n", ch);
  fclose(pf);
  pf = NULL;
  return 0;
}


这里用n来接收偏移量,打印结果:


我们也可以用这个函数来判断文件有多少个字节:


先用fseek将指针移动到末尾

再用ftell函数;


rewind函数

rewind用于将文件的位置指针重置到文件的开始位置。它的功能类似于使用 fseek 函数来将文件指针移动到文件开头,但 rewind 不返回值,因此不能用来检测错误。


1. void rewind(FILE *stream);
2. 1
3. int main()
4. {
5.  FILE* pf = fopen("test2.txt", "r");
6.  if (pf == NULL)
7.  {
8.   perror("fopen");
9.   return 1;
10.   }
11.   int ch = fgetc(pf);
12.   printf("%c\n", ch);
13. 
14.   ch = fgetc(pf);
15.   printf("%c\n", ch);
16. 
17.   ch = fgetc(pf);
18.   printf("%c\n", ch);
19. 
20.   ch = fgetc(pf);
21.   printf("%c\n", ch);
22. 
23.   rewind(pf);
24. 
25.   ch = fgetc(pf);
26.   printf("%c\n", ch);
27. 
28.   fclose(pf);
29.   pf = NULL;
30.   return 0;
31. }


还以这串代码为例,rewind函数调用后,移动指针指向起始位置,打印结果为a



文件读取结束的判定

feof和ferror函数

feof 和 ferror 是用于检查文件状态的两个不同函数,它们分别用于检测文件流的结束-of-file (EOF) 状态和读写错误。

feof

int feof(FILE *stream);


feof 用于检查是否已经读取到文件的末尾。它检查与文件流关联的 EOF 标志位。

如果已经达到文件末尾,返回非零值;否则,返回 0

1. FILE *filePointer = fopen("file.txt", "r");
2. // ... 文件读取操作 ...
3. 
4. if (feof(filePointer)) {
5.     // 已到达文件末尾
6. }
7. 1
8. 2
9. 3
10. 4
11. 5
12. 6
13. ferror
14. 
15. int ferror(FILE *stream)


ferror 用于检查文件流是否因为错误而无法继续读取或写入

如果文件流有错误,返回非零值;否则,返回 0


注意点


EOF and 错误:feof 和 ferror 检查的是不同的情况:feof 是检查是否到达文件末尾,而 ferror 是检查文件操作是否发生错误。

循环中使用:在循环中读取文件时,应当检查这两个函数来确保正确处理文件末尾和可能发生的错误。

feof 的误用:经常有误用 feof 的情况,即在循环条件中直接使用 feof。正确的方法是在读取操作后检查 feof。因为只有在尝试读取超过文件末尾之后,EOF 标志才会被设置。

判断方式

文本文件读取是否结束,判断返回值是否为EOF( fgetc),或者NULL(fgets)

二进制文本的读取结束判断,判断返回值是否小于实际要读的个数,例如

fread判断返回值是否小于实际要求的个数

使用 fgetc 读取文件的示例:

#include 
int main() {
    FILE *file = fopen("example.txt", "r");
    if (file == NULL) {
        perror("fopen");
        return 1;
    }
    int c; // 注意:int 类型用于存储 EOF
    // 使用 fgetc 逐字符读取文件,直到文件结束
    while ((c = fgetc(file)) != EOF) {
        putchar(c); // 输出字符
    }
    // 检查是否因为文件末尾才停止读取
    if (feof(file)) {
        puts("\n文件已读取完毕");
    } else if (ferror(file)) {
        perror("读取文件时发生错误");
    }
    fclose(file);
    return 0;
}


使用 fread 读取文件的示例:

1. #include 
2. 
3. int main() {
4.     FILE *file;
5.     int number;
6. 
7.     // 打开文件用于二进制读取
8.     file = fopen("output.txt", "rb");
9.     if (file == NULL) {
10.         perror("Error opening file");
11.         return 1;
12.     }
13. 
14.     // 使用fread读取二进制数
15.     size_t itemsRead = fread(&number, sizeof(int), 1, file);
16.     if (itemsRead == 1) {
17.         printf("读取的整数是:%d\n", number);
18.     } else {
19.         // 如果没有读取到一个整数,打印错误信息
20.         if (feof(file)) {
21.             printf("文件结束,未读取到数据。\n");
22.         }
23.         if (ferror(file)) {
24.             printf("读取文件时出错。\n");
25.         }
26.     }
27. 
28.     // 关闭文件
29.     fclose(file);
30. 
31.     return 0;
32. }


文件缓冲区

缓冲区在计算机科学中是一块内存区域,用于临时存放数据,目的是在数据在发送者和接收者之间传输时调节和平衡数据流。在 I/O 操作的上下文中,缓冲区的主要作用是减少对硬件设备(如硬盘、网络设备等)的直接访问次数,提高数据处理的效率和吞吐量。


标准库提供的文件操作函数(如 fread、fwrite、printf、scanf 等)通常都会使用这些缓冲区


功能和使用

提高性能:缓冲区可以减少对底层 I/O(输入/输出) 系统的调用次数,因为数据是在缓冲区中累积起来,然后一次性进行读写,这通常可以提高性能。

缓冲区管理:C 标准库提供了一系列函数来管理和控制缓冲区,如 setbuf、setvbuf 等。

刷新缓冲区:在需要时,可以使用 fflush 函数手动刷新输出缓冲区,将缓冲区内的数据写入实际的 I/O 设备中。例如,可能需要在写入文件后立即刷新缓冲区,以确保数据被物理写入磁盘。

关闭文件:在关闭文件时(使用 fclose),缓冲区会自动被刷新。

例如,在 C 中,FILE 结构就关联了一个缓冲区。当你使用 fopen 打开一个文件时,系统会自动分配一个缓冲区,你可以使用 setvbuf 来更改其缓冲行为。当你读写数据时,例如使用 fread 或 fwrite 函数,这些数据会传递通过这个缓冲区,从而提高读写操作的效率。


在文本编辑器中,用户的输入通常存储在缓冲区内直到按下 “保存” 按钮时才写入硬盘。在网络通信中,数据包可能会首先存储在缓冲区内,然后一起发送以减少网络传输开销。在视频流媒体播放中,视频数据可以预先存储在缓冲区内,以避免播放时由于网络延迟导致的卡顿。


本章内容到此结束!感谢大家的观看!!

————————————————


相关文章
|
JavaScript
JS自动生成速记符、拼音简写/拼音的声母(例如:“你挚爱的强哥”转换为“NZADQG”)。提取首字母,返回大写形式;提取拼音, 返回首字母大写形式(全拼)。
JS自动生成速记符、拼音简写/拼音的声母(例如:“你挚爱的强哥”转换为“NZADQG”)。提取首字母,返回大写形式;提取拼音, 返回首字母大写形式(全拼)。
27849 0
|
3月前
|
人工智能 机器人 网络安全
新手必看!阿里云部署OpenClaw保姆级图文步骤+企业微信接入教程+新手避坑指南
在2026年企业数字化与AI自动化深度融合的浪潮中,OpenClaw(原Clawdbot,曾用名Moltbot)凭借开源特性、灵活的插件生态与强大的任务执行能力,成为企业办公、团队协作与个人效率提升的核心工具。这款开源AI智能体框架,截至2026年3月,在GitHub平台星标数量已突破24万,Fork数超4.5万,支持通过自然语言完成文件管理、信息检索、流程自动化、多端协同等多样化任务。而企业微信作为国内领先的企业级协同办公平台,覆盖超6000万企业组织,具备消息触达、客户联系、客户群管理、客户朋友圈、审批流、微盘存储、机器人集成等核心优势,是企业办公、团队协作、内部沟通与客户服务的首选载体。
1059 1
|
3月前
|
安全 Cloud Native Java
吃透 API 网关:从核心原理、架构选型到千万级 QPS 高性能设计实战
API网关是微服务架构的流量中枢,承担统一接入、智能路由、安全防护、流量治理、协议转换与可观测性等核心能力。它解耦客户端与后端服务,提升系统稳定性、安全性与可维护性,是云原生架构的关键基础设施。
470 1
|
存储 SQL 大数据
列式存储系列(一)C-Store
列式存储系列(一)概述 序 本文是列式存储系列的第一篇。在这个系列中,我们将介绍几个典型的列式存储系统。这些列式系统的出现都有各自的时代背景。在介绍这些系统的同时,我们也尽量介绍一下它们的背景,以便大家有一个更宏观的认识,理解这个系统为什么会出现,它要解决的问题,以及它如何影响后来类似系统的发展。
3736 0
|
2月前
|
存储 搜索推荐 应用服务中间件
Searx自建搜索平台全攻略:私有化部署实战指南
Searx是开源免费的元搜索引擎,聚合Google、Bing等70+站点结果,支持视频/图片/磁力搜索;不追踪、不存档用户数据,中文友好,一键部署便捷,适合替代受限搜索引擎。
206 1
Searx自建搜索平台全攻略:私有化部署实战指南
|
2月前
|
弹性计算 运维 大数据
阿里云 ECS vs 轻量应用服务器:区别在哪?如何选择?
阿里云 ECS 是面向企业级用户和专业开发者的 IaaS 级弹性计算服务,资源灵活、性能强、扩展性高,适合高并发、大数据等复杂场景,但运维成本高;轻量应用服务器主打简单易用、一键部署,提供套餐化配置,成本低,适合个人开发者、中小企业搭建轻量级应用。选择时,技术强、业务复杂选 ECS;追求简单、低成本、业务轻量选轻量应用服务器,且业务增长可平滑迁移至 ECS。
583 6
|
3月前
|
存储 缓存 监控
微服务越拆越乱?8 大致命反模式拆解与架构重构全实战
本文剖析微服务常见八大反模式(如分布式单体、数据库共享、过度拆分等),揭示其违背的核心设计原则,并提供可落地的重构方案、代码实现与治理方法论,助团队走出架构乱象,回归微服务本质价值。
245 1
|
人工智能 搜索推荐 小程序
时光有节,岁月有气,用 CodeBuddy + 地图 MCP 构建二十四节气
二十四节气作为中国古老智慧的结晶,不仅指导农耕生活,更蕴含深厚文化意义。文章以“小满”为例,解读其象征的生活哲学,并探讨如何借助现代科技如CodeBuddy,将这一传统时间体系融入日常生活。通过制作“二十四节气速查表”,结合天气API和地图功能,让节气焕发新生,成为连接自然与生活的桥梁。这不仅是对文化遗产的传承,更是对传统文化的创新表达。
|
存储 内存技术
内存条RAM详细指南
内存条(RAM)是电脑中用于临时存储数据和程序的部件,CPU依赖它执行操作。内存条经历了从主内存扩展到读写内存整体的发展,常见类型包括SDRAM和DDR SDRAM。内存容量、存取时间和奇偶校验是衡量其性能的关键指标。在选购时,应考虑类型、容量、速度和品牌,知名品牌的内存条提供更好的可靠性和稳定性。
6042 2
|
存储 监控 Java
内存泄漏及其解决方法
内存泄漏及其解决方法
414 0