C语言——⽂件操作(1)

耕耘 :C、C++、嵌入式技术领域

🔥我的个人主页

❄️个人专栏:《C语言专栏》 《嵌入式专栏》

✨**不要等待机会,而要创造机会!**✨

📽博主简介:

✨✨一位热爱生活的阳光大男孩.✨✨

前言

本文系统讲解C语言文件操作核心知识,涵盖文件概念、二进制与文本文件区别、流与文件指针机制,重点解析fopen、fclose函数使用及常见错误处理。通过实例演示文件顺序读写(如fputc),并强调打开文件后必须判断返回值、关闭文件时置空指针等最佳实践,为掌握文件操作提供完整技术框架。

文章目录

  • 前言
    • [1. 为什么使⽤⽂件?](#1. 为什么使⽤⽂件?)
    • [2. 什么是⽂件?](#2. 什么是⽂件?)
    • [2.1 程序⽂件](#2.1 程序⽂件)
    • [2.2 数据⽂件](#2.2 数据⽂件)
    • [2.3 ⽂件名](#2.3 ⽂件名)
  • [3. ⼆进制⽂件和⽂本⽂件](#3. ⼆进制⽂件和⽂本⽂件)
  • [4. ⽂件的打开和关闭](#4. ⽂件的打开和关闭)
    • [4.1 流和标准流](#4.1 流和标准流)
      • [4.1.1 流](#4.1.1 流)
      • [4.1.2 标准流](#4.1.2 标准流)
    • [4.2 ⽂件指针](#4.2 ⽂件指针)
    • [4.3 ⽂件的打开和关闭](#4.3 ⽂件的打开和关闭)
      • [4.3.1 fopen](#4.3.1 fopen)
      • [4.3.2 fclose](#4.3.2 fclose)
  • [5. ⽂件的顺序读写](#5. ⽂件的顺序读写)
    • [5.1 fputc](#5.1 fputc)
    • [5.2 fgetc](#5.2 fgetc)
    • [5.3 feof和ferror](#5.3 feof和ferror)
    • [5.4 fputs](#5.4 fputs)
    • [5.5 fgets](#5.5 fgets)
    • [5.6 fprintf](#5.6 fprintf)
    • [5.7 fscanf](#5.7 fscanf)
    • [5.8 fwrite](#5.8 fwrite)
    • [5.9 fread](#5.9 fread)
    • [5.10 对⽐⼀组函数:](#5.10 对⽐⼀组函数:)
      • [5.10.1 sprintf](#5.10.1 sprintf)
      • [5.10.2 sscanf](#5.10.2 sscanf)
  • 结语

1. 为什么使⽤⽂件?

如果没有⽂件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运⾏程序,是看不到上次程序的数据的,如果要将数据进⾏持久化的保存,我们可以使⽤⽂件。

2. 什么是⽂件?

磁盘(硬盘)上的⽂件是⽂件。但是在程序设计中,我们⼀般谈的⽂件有两种:程序⽂件、数据⽂件(从⽂件功能的⻆度来分类的)。

2.1 程序⽂件

程序⽂件包括源程序⽂件(后缀为.c),⽬标⽂件(windows环境后缀为.obj),可执⾏程序(windows环境后缀为.exe)。

2.2 数据⽂件

⽂件的内容不⼀定是程序,⽽是程序运⾏时读写的数据,⽐如程序运⾏需要从中读取数据的⽂件,或者输出内容的⽂件。

本章讨论的是数据⽂件。

在以前各章所处理数据的输⼊输出都是以终端为对象的,即从终端的键盘输⼊数据,运⾏结果显⽰到显⽰器上。

其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使⽤,这⾥处理的就是磁盘上⽂件。

2.3 ⽂件名

⼀个⽂件要有⼀个唯⼀的⽂件标识,以便⽤⼾识别和引⽤。⽂件名包含3部分:⽂件路径+⽂件名主⼲+⽂件后缀

例如: c:\code\test.txt

为了⽅便起⻅,⽂件标识常被称为⽂件名。

3. ⼆进制⽂件和⽂本⽂件

根据数据的组织形式,数据⽂件被分为**⽂本⽂件和⼆进制⽂件** 。

数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的⽂件中,就是⼆进制⽂件。

如果要求在内存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是⽂本⽂件。

⼀个数据在⽂件中是怎么存储的呢?

字符⼀律以ASCII形式存储,数值型数据既可以⽤ASCII形式存储,也可以使⽤⼆进制形式存储。

如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽⼆进制形式输出,则在磁盘上只占4个字节。

4. ⽂件的打开和关闭

4.1 流和标准流

4.1.1 流

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。

总结:流就是数据像水一样,持续、有序地流动,让程序能边接收边处理,而不是等全部准备好才开始。这在处理大文件、实时音视频、网络通信中特别重要。

4.1.2 标准流

那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?

那是因为C语⾔程序在启动的时候,默认打开了3个流:

• stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。

• stdout - 标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出流中。

• stderr - 标准错误流,⼤多数环境中输出到显⽰器界⾯。

这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。

stdin 、 stdout 、 stderr 三个流的类型是: FILE * ,通常称为⽂件指针。

*C语⾔中,就是通过 FILE 的⽂件指针来维护流的各种操作的。**

4.2 ⽂件指针

缓冲⽂件系统中,关键的概念是"⽂件类型指针",简称**"⽂件指针"**。每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名 FILE 。

例如,VS2013 编译环境提供的 stdio.h 头⽂件中有以下的⽂件类型申明:

c 复制代码
struct _iobuf
{
	char* _ptr;
	int _cnt;
	char* _base;
	int _flag;
	int _file;
	int _charbuf;
	int _bufsiz;
	char* _tmpfname;
};
typedef struct _iobuf FILE;

不同的C编译器的FILE类型包含的内容不完全相同,但是⼤同⼩异。⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使⽤起来更加⽅便

下⾯我们可以创建⼀个FILE*的指针变量

c 复制代码
FILE* pf;//⽂件指针变量

定义pf是⼀个指向FILE类型数据的指针变量。可以使pf指向某个⽂件的⽂件信息区(是⼀个结构体变量)。通过该⽂件信息区中的信息就能够访问该⽂件。也就是说,通过⽂件指针变量能够间接找到与它关联的⽂件 。

4.3 ⽂件的打开和关闭

⽂件在读写之前应该先打开⽂件,在使⽤结束之后应该关闭⽂件。

在编写程序的时候,在打开⽂件的同时,都会返回⼀个FILE*的指针变量指向该⽂件,也相当于建⽴了指针和⽂件的关系。

ANSI C 规定使⽤ fopen 函数来打开⽂件, fclose 来关闭⽂件。

4.3.1 fopen

c 复制代码
//打开⽂件
FILE * fopen ( const char * filename, const char * mode );

功能: fopen 函数是⽤来打开参数 filename 指定的⽂件,同时将打开的⽂件和⼀个流进⾏关联,后续对流的操作是通过 fopen 函数返回的指针来维护。具体对流(关联的⽂件)的操作是通过参数mode 来指定的。

参数:

filename :表⽰被打开的⽂件的名字,这个名字可以绝对路径,也可以是相对路径。

mode :表⽰对打开的⽂件的操作⽅式,具体⻅下⾯的表格。

返回值:
• 若⽂件成功打开,该函数将返回⼀个指向 FILE 对象的指针,该指针可⽤于后续操作中标识对应的流。
• 若打开失败,则返回 NULL 指针,所以⼀定要对 fopen 的返回值做判断,来验证⽂件是否打开成功。

代码演⽰:

c 复制代码
#include <stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "r"); //以"r"的形式打开⽂件,如果⽂件不存在,则打开失败
		if (pf == NULL)
		{
			perror("fopen\n");
			return 1;
		}
	printf("打开⽂件成功,可以对⽂件进⾏操作\n");
	//不再使⽤⽂件时,需要关闭⽂件
	return 0;
}

mode表⽰对打开的⽂件的操作⽅式:

4.3.2 fclose

c 复制代码
//fclose函数原型
int fclose ( FILE * stream );

功能:关闭参数 stream 关联的⽂件,并取消其关联关系。与该流关联的所有内部缓冲区均会解除关

联并刷新:任何未写⼊的输出缓冲区内容将被写⼊,任何未读取的输⼊缓冲区内容将被丢弃。

参数:

stream :指向要关闭的流的 FILE 对象的指针

返回值:成功关闭 stream 指向的流会返回0,否则会返回 EOF 。

代码演⽰:

c 复制代码
#include <stdio.h>
int main()
{
	FILE* fp = fopen("test.txt", "r"); //以"r"的形式打开⽂件,如果⽂件不存在,则打开失败
		if (fp == NULL)
		{
			perror("fopen\n");
			return 1;
		}
	printf("打开⽂件成功,可以对⽂件进⾏操作\n");
	//不再使⽤⽂件时,需要关闭⽂件
	fclose(fp);
	fp = NULL; //将指针置为NULL,避免成为野指针。
	return 0;
}

5. ⽂件的顺序读写

上⾯说的适⽤于所有输⼊流⼀般指适⽤于标准输⼊流和其他输⼊流(如⽂件输⼊流);所有输出流⼀般指适⽤于标准输出流和其他输出流(如⽂件输出流)。

5.1 fputc

c 复制代码
//fputc函数原型
int fputc ( int character, FILE * stream );

功能:将参数 character 指定的字符写⼊到 stream 指向的输出流中,通常⽤于向⽂件或标准输出流写⼊字符。在写⼊字符之后,还会调整指⽰器。字符会被写⼊流内部位置指⽰器当前指向的位置,随后该指⽰器⾃动向前移动⼀个位置。

参数:

character :被写⼊的字符

stream :是⼀个FILE*类型的指针,指向了输出流(通常是⽂件流或 stdout )。

返回值:
• 成功时返回写⼊的字符(以 int 形式)。
• 失败时返回 EOF (通常是 -1),错误指⽰器会被设置,可通过 ferror() 检查具体错误。

代码演⽰:

c 复制代码
#include <stdio.h>
int main()
{
	FILE* fp = fopen("test.txt", "w"); //以w的形式打开⽂件,才能正确的写⽂件
	if (fp == NULL)
	{
		perror("fopen\n");
		return 1;
	}
	fputc('a', fp);
	fputc('b', fp);
	fputc('c', fp);
	//不再使⽤⽂件时,需要关闭⽂件
	fclose(fp);
	fp = NULL; //将指针置为NULL,避免成为野指针。
	return 0;
}
//也可以循环写⼊多个字符的
#include <stdio.h>
int main()
{
	FILE* fp = fopen("test.txt", "w"); //以w的形式打开⽂件,才能正确的写⽂件
	if (fp == NULL)
	{
		perror("fopen\n");
		return 1;
	}
	int ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, fp);
	}
	//不再使⽤⽂件时,需要关闭⽂件
	fclose(fp);
	fp = NULL; //将指针置为NULL,避免成为野指针。
	return 0;
}

5.2 fgetc

c 复制代码
//fgetc函数的原型
int fgetc ( FILE * stream );

功能:从参数 stream 指向的流中读取⼀个字符。函数返回的是⽂件指⽰器当前指向的字符,读取这个字符之后,⽂件指⽰器⾃动前进到下⼀个字符。

参数:

stream : FILE*类型的⽂件指针,可以是 stdin ,也可以是其他输⼊流的指针。如果是 stdin 就从标准输⼊流读取数据。如果是⽂件流指针,就从⽂件读取数据。

返回值:
• 成功时返回读取的字符(以 int 形式)。
• 若调⽤时流已处于⽂件末尾,函数返回 EOF 并设置流的⽂件结束指⽰器( feof )。
若发⽣读取错误,函数返回 EOF 并设置流的错误指⽰器( ferror )。

代码演⽰:

c 复制代码
#include <stdio.h>
int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		perror("fopen\n");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		int c = fgetc(fp);
		fputc(c, stdout); //使⽤fputc在标准输出流上打印字符
	}
	//不再使⽤⽂件时,需要关闭⽂件
	fclose(fp);
	fp = NULL; //将指针置为NULL,避免成为野指针。
	return 0;
}

5.3 feof和ferror

c 复制代码
int feof ( FILE * stream );
//检测stream指针指向的流是否遇到⽂件末尾
int ferror ( FILE * stream );
//检测stream指针指向的流是否发⽣读/写错误

如果在读取⽂件的过程中,遇到了⽂件末尾,⽂件读取就会结束。这时读取函数会在对应的流上设置⼀个⽂件结束的指⽰符,这个⽂件结束指⽰符可以通过 feof 函数检测到。如果 feof 函数检测到⽂件结束指⽰符已经被设置,则返回⾮0的值,如果没有设置则返回0。

如果在读/写⽂件的过程中,发⽣了读/写错误,⽂件读取就会结束。这时读/写函数会在对应的流上设置⼀个错误指⽰符,这个错误指⽰符可以通过 ferror 函数检测到。如果 ferror 函数检测到错误指⽰符已经被设置,则返回⾮0的值,如果没有设置则返回0。

c 复制代码
测试feof函数
#include <stdio.h>
//假设test.txt⽂件中存放abcdef
int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		perror("fopen\n");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		int c = fgetc(fp);
		if (c == EOF)
		{
			if (feof(fp))
				printf("遇到⽂件末尾了\n");
			else if (ferror(fp))
				printf("读取发⽣了错误\n");
		}
		else
		{
			fputc(c, stdout); //使⽤fputc在标准输出流上打印字符
		}
	}

		//不再使⽤⽂件时,需要关闭⽂件
		fclose(fp);
	fp = NULL; //将指针置为NULL,避免成为野指针。
	return 0;
}
c 复制代码
测试ferror函数
#include <stdio.h>
//以写的形式打开⽂件后,再去读⽂件,就会发⽣错误
int main()
{
	FILE* fp = fopen("test.txt", "w");
	if (fp == NULL)
	{
		perror("fopen\n");
		return 1;
	}
	int c = fgetc(fp);//读⽂件
	if (c == EOF)
	{
		if (feof(fp))
			printf("遇到⽂件末尾了\n");
		else if (ferror(fp))
		{
			printf("读⽂件发⽣了错误\n");
		}
	}
	else
	{
		fputc(c, stdout); //使⽤fputc在标准输出流上打印字符
	}
	//不再使⽤⽂件时,需要关闭⽂件
	fclose(fp);
	fp = NULL; //将指针置为NULL,避免成为野指针。
	return 0;
}

5.4 fputs

c 复制代码
fputs 函数原型
int fputs ( const char * str, FILE * stream );

功能:将参数 str 指向的字符串写⼊到参数 stream 指定的流中(不包含结尾的 \0 ),适⽤于⽂件流或标准输出(stdout)。

参数:

str : str是指针,指向了要写⼊的字符串(必须以 \0 结尾)

stream :是⼀个 FILE* 的指针,指向了要写⼊字符串的流。

返回值:
• 成功时返回⾮负整数。
• 失败时返回 EOF (-1),同时会设置流的错误指⽰器,可以使⽤ ferror() 检查错误原因。

代码演⽰:

c 复制代码
#include <stdio.h>
int main()
{
	FILE* fp = fopen("test.txt", "w");
	if (fp == NULL)
	{
		perror("fopen\n");
		return 1;
	}
	int i = 0;
	fputs("abc\0def", fp);
	fputs("hehe", fp);
	//不再使⽤⽂件时,需要关闭⽂件
	fclose(fp);
	fp = NULL; //将指针置为NULL,避免成为野指针。
	return 0;
}

5.5 fgets

c 复制代码
fgets函数原型
char * fgets ( char * str, int num, FILE * stream );

功能:从 stream 指定输⼊流中读取字符串,⾄读取到换⾏符、⽂件末尾(EOF)或达到指定字符数(包含结尾的空字符 \0 ),然后将读取到的字符串存储到str指向的空间中。

参数:

• str :是指向字符数组的指针,str指向的空间⽤于存储读取到的字符串。

• num :最⼤读取字符数(包含结尾的 \0 ,实际最多读取 num-1 个字符)。

• stream :输⼊流的⽂件指针(如⽂件流或 stdin )。

返回值:
• 成功时返回 str 指针。
• 若在尝试读取字符时遇到⽂件末尾,则设置⽂件结束指⽰器,并返回 NULL ,需通过 feof()检测。
• 若发⽣读取错误,则设置流错误指⽰器,并返回 NULL,通过 ferror() 检测。

代码演⽰:

c 复制代码
#include <stdio.h>
int main()
{
	FILE* fp = fopen("test.txt", "r");//test.txt放的是123456789
	if (fp == NULL)
	{
		perror("fopen\n");
		return 1;
	}
	char arr1[] = "*************";
	fgets(arr1, sizeof(arr1), fp);
	char arr2[] = "*************";
	fgets(arr2, sizeof(arr1), fp);
	//不再使⽤⽂件时,需要关闭⽂件
	fclose(fp);
	fp = NULL; //将指针置为NULL,避免成为野指针。
	return 0;
}

使⽤细节:

• 若读取到换⾏符( \n ),会将其包含在字符串中(除⾮超出 num-1 限制),然后以 \0 结尾。

• ⽂件末尾⽆换⾏符时,字符串以 \0 结尾,不包含 \n 。

5.6 fprintf

c 复制代码
int fprintf ( FILE * stream, const char * format, ... );
//类⽐printf函数学习

功能: fprintf 是将格式化数据写⼊指定⽂件流的函数。它与 printf 类似,但可以输出到任意输出流(如磁盘⽂件、标准输出、标准错误等),⽽不仅限于控制台。

参数:

• stream :指向 FILE 对象的指针,表⽰要写⼊的⽂件流(如 stdout 、⽂件指针等)。

• format :格式化字符串,包含要写⼊的⽂本和格式说明符(如 %d 、 %s 等)。

• ... :可变参数列表,提供与格式字符串中说明符对应的数据。

返回值:
• 成功时,返回写⼊的字符总数(⾮负值)。
• 失败时,先设置对应流的错误指⽰器,再返回负值,可以通过 ferror() 来检测。

代码演⽰:

c 复制代码
#include <stdio.h>
struct Stu
{
	char name[30];
	int age;
	float score;
};
int main()
{
	struct Stu b = { "zhangsan", 20, 85.5f };
	FILE* fp = fopen("test.txt", "w");
	if (fp == NULL)
	{
		perror("fopen\n");
		return 1;
	}
	fprintf(fp, "%s %d %.1f", b.name, b.age, b.score);
	//不再使⽤⽂件时,需要关闭⽂件
	fclose(fp);
	fp = NULL; //将指针置为NULL,避免成为野指针。
	return 0;
}

5.7 fscanf

c 复制代码
int fscanf ( FILE * stream, const char * format, ... );
//类⽐scanf函数来学习

功能: fscanf 是从指定⽂件流中读取格式化数据的函数。它类似于 scanf ,但可以指定输⼊源(如⽂件、标准输⼊等),⽽⾮仅限于控制台输⼊。适⽤于从⽂件解析结构化数据(如整数、浮点数、字符串等)。

参数:

• stream :指向 FILE 对象的指针,表⽰要读取的⽂件流(如 stdin 、⽂件指针等)。

• format :格式化字符串,定义如何解析输⼊数据(如 %d 、 %f 、 %s 等)。

• ... :可变参数列表,提供存储数据的变量地址(需与格式字符串中的说明符匹配)。

返回值:
• 成功时,函数返回成功填充到参数列表中的项数。该值可能与预期项数⼀致,也可能因以下原因少于预期(甚⾄为零):
◦ 格式和数据匹配失败;
◦ 读取发⽣错误;
◦ 到达⽂件末尾(EOF)。
• 如果在成功读取任何数据之前发⽣:
◦ 发⽣读取错误,会在对应流上设置错误指⽰符,则返回 EOF 。
◦ 到达⽂件末尾,会在对应流上设置⽂件结束指⽰符,则返回 EOF 。

代码演⽰:

假设在fprintf函数学习的时候,已经向⽂件中写⼊了数据: zhangsan 20 85.5 ,我们现在想在⽂件中读取这些数据出来。

c 复制代码
#include <stdio.h>
struct Stu
{
	char name[30];
	int age;
	float score;
};
int main()
{
	struct Stu s = { 0 };
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		perror("fopen\n");
		return 1;
	}
	fscanf(fp, "%s %d %f", s.name, &(s.age), &(s.score));//按照格式读取数据
	fprintf(stdout, "%s %d %.1f\n", s.name, s.age, s.score);//打印数据在stdout上
	//不再使⽤⽂件时,需要关闭⽂件
	fclose(fp);
	fp = NULL; //将指针置为NULL,避免成为野指针。
	return 0;
}

5.8 fwrite

c 复制代码
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

功能:函数⽤于将数据块写⼊ stream 指向的⽂件流中,是以2进制的形式写⼊的。

参数:
• ptr :指向要写⼊的数据块的指针。
• size :要写⼊的每个数据项的⼤⼩(以字节为单位)。
• count :要写⼊的数据项的数量。
• stream :指向 FILE 类型结构体的指针,指定了要写⼊数据的⽂件流。

返回值:返回实际写⼊的数据项数量。如果发⽣错误,则返回值可能⼩于 count 。

代码演⽰:

假设要将⼀组整数写⼊到⽂件 "data.bin" 中:

c 复制代码
#include <stdio.h>
int main()
{
	int data[] = { 1, 2, 3, 4, 5 };
	// 打开⽂件
	FILE* fp = fopen("data.bin", "wb");
	// 检查⽂件是否成功打开
	if (fp == NULL)
	 {
		perror("fopen");
		return 1;
	}
	// 将数据写⼊⽂件
	if (fwrite(data, sizeof(int), 5, fp) != 5) 
	{
		perror("fwrite");
		return -1;
	}
	// 关闭⽂件
	fclose(fp);
	fp = NULL;
	return 0;
}

5.9 fread

c 复制代码
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

功能:函数⽤于从 stream 指向的⽂件流中读取数据块,并将其存储到 ptr 指向的内存缓冲区中。

参数:

• ptr :指向内存区域的指针,⽤于存储从⽂件中读取的数据。

• size :要读取的每个数据块的⼤⼩(以字节为单位)。

• count :要读取的数据块的数量。

• stream :指向 FILE 类型结构体的指针,指定了要从中读取数据的⽂件流。

返回值:返回实际读取的数据块数量。

使⽤注意事项:

• 需要包含 <stdio.h> 头⽂件。

• 在使⽤ fread() 之前,需要确保⽂件已经以⼆进制可读⽅式打开。

• ptr 指向的内存区域必须⾜够⼤,以便存储指定数量和⼤⼩的数据块。

• 如果 fread() 成功读取了指定数量的数据块,则返回值等于 count ;如果读取数量少于count ,则可能已经到达⽂件结尾或者发⽣了错误。

• 在⼆进制⽂件读取时, fread() 是常⽤的函数,但对于⽂本⽂件读取,通常使⽤ fgets()或 fscanf() 。

代码演⽰:

假设有⼀个⼆进制⽂件 "data.bin",包含⼀些整数数据,我们将使⽤ fread() 函数读取这些数据:

c 复制代码
#include <stdio.h>
int main()
{
	int data[5] = { 0 }; // 假设⽂件中包含 5 个整数数据
	// 打开⽂件
	FILE* fp = fopen("data.bin", "rb");
	// 检查⽂件是否成功打开
	if (fp == NULL) {
		perror("fopen");
		return -1;
	}
	// 从⽂件中读取整数数据块
	size_t num_read = fread(data, sizeof(int), 5, fp);
	// 检查读取是否成功
	if (num_read != 5)
	{
		if (feof(fp))
			printf("Reached end of file\n");
		else if (ferror(fp))
			printf("Error reading file\n");
	}
	else
	{
		// 输出读取的数据
		for (int i = 0; i < 5; i++) {
			printf("Data[%d]: %d\n", i, data[i]);
		}
	}
	// 关闭⽂件
	fclose(fp);
	return 0;
}

5.10 对⽐⼀组函数:

scanf/fscanf/sscanf

printf/fprintf/sprintf

5.10.1 sprintf

c 复制代码
int sprintf ( char * str, const char * format, ... );

功能:将格式化数据写⼊字符数组(字符串)。它类似于 printf ,但输出⽬标不是控制台或⽂件,⽽是⽤⼾指定的内存缓冲区。常⽤于动态⽣成字符串、拼接数据或转换数据格式。简⽽⾔之就是将格式化的数据转换成⼀个字符串。

参数:

• str :指向字符数组的指针,⽤于存储⽣成的字符串(需确保⾜够⼤以防⽌溢出)。

• format :格式化字符串,定义输出格式(如 %d 、 %f 、 %s 等)。

• ... :可变参数列表,提供与格式字符串中说明符对应的数据。

返回值:

• 成功时:返回写⼊ buffer 的字符数(不包括结尾的空字符 \0 )。

• 失败时:返回负值。

代码演⽰:

c 复制代码
#include <stdio.h>
struct Stu
{
	char name[30];
	int age;
	float score;
};
int main()
{
	struct Stu s = { "zhangsan", 100, 85.5f };
	//将s中的结构数据转换成⼀个字符串
	char arr[100] = { 0 };
	sprintf(arr, "%s %d %.1f", s.name, s.age, s.score);
	printf("%s\n", arr);
	return 0;
}

5.10.2 sscanf

c 复制代码
int sscanf ( const char * str, const char * format, ...);

功能:从字符串中读取格式化数据。它与 scanf 类似,但输⼊源是内存中的字符串⽽⾮控制台或⽂件。常⽤于解析字符串中的结构化数据(如提取数字、分割⽂本等)。

参数:

• str :要解析的源字符串(输⼊数据来源)。

• format :格式化字符串,定义如何解析数据(如 %d 、 %f 、 %s 等)。

• ... :可变参数列表,提供存储数据的变量地址(需与格式字符串中的说明符匹配)

返回值:

• 成功时:返回成功解析并赋值的参数数量(⾮负值)。

• 失败或未匹配任何数据:若输⼊结束或解析失败,返回 EOF (通常是 -1 )。

代码演⽰:

c 复制代码
#include <stdio.h>
struct Stu
{
	char name[30];
	int age;
	float score;
};
int main()
{
	char str[] = "zhangsan 100 85.5";
	struct Stu s = { 0 };
	//从str中的字符串内中解析出格式化的数据,存放在结构体中
	sscanf(str, "%s %d %f", s.name, &(s.age), &(s.score));
	//打印结构体s中的数据
	printf("%s %d %.1f\n", s.name, s.age, s.score);
	return 0;
}

结语

续.

相关推荐
ychqsq1 小时前
198.根源
经验分享·职场和发展
狗凯之家源码网1 小时前
PHP 开源 IM 即时通讯系统效果实测与功能展示
开发语言·开源·php
anew___1 小时前
《从零手写操作系统 (11):文件系统初探——initrd与VFS抽象层》
java·javascript·算法
木由里予1 小时前
GDB 使用手册(详细版)
c语言·vscode
青绿蓝LCA低碳研究院1 小时前
警惕“碳隧道视野”,见木不见林 · 青绿蓝
经验分享
辛苦才能1 小时前
C++ map/set 深度解析:从关联式容器的本质到 operator[] 的三重身份
开发语言·c++
Chen—LSN2 小时前
C语言——文件操作(2)
c语言·开发语言·c++·经验分享·笔记·算法·c#
纪念 2292 小时前
C++类和对象(最终篇)
开发语言·c++
程序员yu2 小时前
会编网络:别被入门教程骗了,Python实用能力不在语法本身
开发语言·python·学习·算法