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

简介:

函数对比

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 函数,这些数据会传递通过这个缓冲区,从而提高读写操作的效率。


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


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

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


相关文章
|
5月前
|
Cloud Native 容器
ldif 数据转成正确的组织结构再探
ldif 数据转成正确的组织结构再探
Threejs实现模拟管道液体流动
Threejs实现模拟管道液体流动
1605 0
Threejs实现模拟管道液体流动
|
9月前
[全民写端]#8简化代码
简化端名和版本
40 0
|
9月前
|
存储 缓存 监控
IO流的实际应用场景及其重要性
IO流(Input/Output Stream)是计算机领域中常见的概念,它负责处理计算机与外部设备之间的数据传输。在实际应用中,IO流有着丰富的应用场景,对于数据的读写操作起着至关重要的作用。本文将详细介绍IO流的实际应用场景,并探讨其在不同领域中的重要性。
404 0
|
数据可视化 大数据 数据管理
带你读《数据资产》第二章数据资产的相关概念2.4 大数据时代的到来和兴起(一)
带你读《数据资产》第二章数据资产的相关概念2.4 大数据时代的到来和兴起
|
机器学习/深度学习 智能设计 自然语言处理
无尽流场景优化总结
无尽流场景优化总结
162 0
无尽流场景优化总结
|
存储 Oracle 大数据
带你读《数据资产》第二章数据资产的相关概念2.4 大数据时代的到来和兴起(一)
带你读《数据资产》第二章数据资产的相关概念2.4 大数据时代的到来和兴起(一)
|
机器学习/深度学习 编解码 5G
前传感知的协作传输和接收之上行链路 | 带你读《5G系统关键技术详解》之十二
本节说明了上行链路和下行链路 C-RAN 的波束成形 设计技术,并将 C-RAN 用户的理论可实现速率表征为前传容量限制的函数。
前传感知的协作传输和接收之上行链路   | 带你读《5G系统关键技术详解》之十二