首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >文件操作入门(上)—— 文件类型及顺序读写基础(含打开关闭)

文件操作入门(上)—— 文件类型及顺序读写基础(含打开关闭)

作者头像
星轨初途
发布2026-01-09 14:37:45
发布2026-01-09 14:37:45
1870
举报
文章被收录于专栏:星轨初途星轨初途

前言

嗨(❁´◡`❁)✲゚,我们已经学习C语言的许多功能,今天我们来学习一些不一样的——用C语言对文件进行操作的重要知识点,让我们来该共同学习吧

在这里插入图片描述
在这里插入图片描述

一、文件使用的原因及目的

1、何为文件

概念:磁盘(硬盘)上的文件是文件。

2、为什么使用文件

如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件。 就像我们对文件进行操作,下次我们再进行查看时文件会保留我们的修改,而我们对程序中的的值进行scanf赋值,下一次运行却不是我们上次赋的值

3、文件的种类

程序设计中,我们⼀般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。

(1)程序文件

程序文件包括源程序文件(后缀为.c),目标文件(windows环境后为.obj),可执行程序(windows环境后缀为.exe)。

(2)数据文件

文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。 我们本篇讨论的是数据文件 在以前各章所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。 其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件。

4、文件名

一个文件要有唯一的文件标识,以便用户识别和引用。 文件名包含三部分:文件路径+文件名主干+文件名后缀 比如,C:\Users\lenovo\Desktop就是一个文件名。 为了方便起见,文件标识常被称为文件名。

在这里插入图片描述
在这里插入图片描述

二、二进制文件和文本文件

根据数据的组织形式,数据文件分为二进制文件文本文件

  • 二进制文件:数据在内存中以二进制的形式存储,如果不加转换地输出到外存(磁盘)的文件中
  • 文本文件:如果要求在外存上以ASCLL码的形式存储,则需要在存储前转换,以ASCLL字符的形式存储的文件

数据在文件中的存储方式

字符一律以ASCll形式进行存储,数值类型既可以用ASCll形式进行存储,也可以使用二进制进行存储 比如整型10000进行存储, 如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节), 而二进制形式输出,则在磁盘上只占4个字节。

在这里插入图片描述
在这里插入图片描述

这里进行操作,不必理解为什么这样及函数的作用,这里只是对文件进行操作以2进制存储,重点在2进制文件,后面会讲实现

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	int a= 10000;
	FILE* pf = fopen("tes.txt", "wb");//打开文件
	fwrite(&a, 4, 1, pf);//存储
	fclose(pf);//关闭
	pf = NULL;//为空指针,避免成为野指针
	return 0;
}

这里出现我们创建的文件

在这里插入图片描述
在这里插入图片描述

直接打开,发现为乱码,这是因为我们以2进制的形式进行存储,记事本以字符的形式进行存储,无法识别

在这里插入图片描述
在这里插入图片描述

这时我们通过VS进行打开

在这里插入图片描述
在这里插入图片描述

结果如下,我们发现就是就是2进制,我们转换为10进制后就是为10000

在这里插入图片描述
在这里插入图片描述

三、文件的打开和关闭

在进行文件的打开和关闭前我们需要先了解流和标准流的概念

1、流和标准流

(1) 流

概念:我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。

C程序针对文件、画面、键盘等的数据输入输出操作都是通过流操作的。 一般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。

(2) 标准流

那为什么我们在键盘上输入数据,向屏幕上输出数据,却没有打开流呢? 这是因为C语言程序在启动的时候就默认打开了3个流,分别是:

标准流名称

描述

关联函数

目标设备

stdin

标准输入流,用于读取用户输入

scanf

键盘

stdout

标准输出流,用于输出程序信息

printf

显示器

stderr

标准错误流,用于输出错误/异常信息

-(可配合 fprintf 使用)

显示器

  • stdin 是程序的“输入通道”,默认从键盘读取数据(如 scanf 就是从 stdin 中获取输入);
  • stdout 是程序的“正常输出通道”,默认将信息打印到显示器(如 printf 的内容会输出到 stdout);
  • stderr 是程序的“错误输出通道”,专门用于输出错误、异常信息(例如程序崩溃提示、逻辑错误提示等),和 stdout 一样默认输出到显示器,但在实际场景中(如日志重定向)可与 stdout 区分处理。

这是默认打开了这三个流,我们使用scanf、printf等函数就可以直接进行输入输出操作的。 stdin、stdout、.stderr三个流的类型是:FILE*,通常称为文件指针。 C语言中,就是通过FILE*的文件指针来维护流的各种操作的。

2、文件指针

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

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

不同的C编译器的FLE类型包含的内容不完全相同,但是大同小异。 每当打开一个文件的时候,系统会根据文件的情况自动创建一个FLE结构的变量,并填充其中的信息使用者不必关心细节。 一般都是通过一个FLE的指针来维护这个FLE结构的变量,这样使用起来更加方便。 下面我们可以创建一个FLE*的指针变量

代码语言:javascript
复制
FILE* pf;    //文件指针变量

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

在这里插入图片描述
在这里插入图片描述

3、文件的打开和关闭

我们通常对文件进行的操作:打开文件->读/写文件->关闭文件 编写程序时打开文件,都会返回应该FILE*的指针变量指向该文件,相当于建立了指针和文件的关系。 使用fopen函数打开文件,fclose来关闭文件

代码语言:javascript
复制
//打开文件
FILE * fopen ( const char * filename, const char * mode );//filename文件名,mode 操作
//关闭文件
int fclose ( FILE * stream );
(1) fopen

(1)函数功能: 打开在参数 filename 中指定其名称的文件,并将其与流相关联,该流可以在将来的作中通过返回的 FILE 指针进行标识。 流上允许的作及其执行方式由 mode 参数定义。 (2)参数: filename:表示被打开的文件的名字,这个名字可以是绝对路径,也可以是相对路径;(路径下面会专门讲) mode表示文件的打开方式,文件的打开模式在下面会有展示 (3)返回值 如果成功打开文件,该函数将返回指向 FILE 对象的指针,该对象可用于在将来的作中标识流。 否则,将返回空指针

mode文件的打开方式

文件使用方式

含义

如果指定文件不存在

“r”(只读)

为了输入数据,打开一个已经存在的文本文件

出错

“w”(只写)

为了输出数据,打开一个文本文件

建立一个新的文件

“a”(追加)

向文本文件尾添加数据

建立一个新的文件

“rb”(只读)

为了输入数据,打开一个二进制文件

出错

“wb”(只写)

为了输出数据,打开一个二进制文件

建立一个新的文件

“ab”(追加)

向一个二进制文件尾添加数据

建立一个新的文件

“r+”(读写)

为了读和写,打开一个文本文件

出错

“w+”(读写)

为了读和写,建立一个新的文件

建立一个新的文件

“a+”(读写)

打开一个文件,在文件尾进行读写

建立一个新的文件

“rb+”(读写)

为了读和写打开一个二进制文件

出错

“wb+”(读写)

为了读和写,新建一个新的二进制文件

建立一个新的文件

“ab+”(读写)

打开一个二进制文件,在文件尾进行读和写

建立一个新的文件

代码

代码语言:javascript
复制
#include <stdio.h>
int main ()
{
    FILE * pFile;
    //打开文件
    pFile = fopen ("text.text","w");
    if(pF==NULL)
    {
      perror("fopen");
      return 1;
    }
    
    return 0;
}

这里我们对pFile = fopen (“text.text”,“w”);中文件的地址进行讲解

路径
  1. 如果在与当前程序在同一路径,直接写文件名就行啦
  2. 相对路径 '.'当前路径 '..'表示上一路径

上一路径就是下图箭头所指

在这里插入图片描述
在这里插入图片描述

展示如下 当前路径

代码语言:javascript
复制
pFile = fopen("text.txt", "w");

上一路径

代码语言:javascript
复制
pFile = fopen("./../text.txt", "w");

同理,前一路径

代码语言:javascript
复制
pFile = fopen("./../../text.txt", "w");

但是防止出现转义字符影响,所以我们用2个//防止转义 如下

代码语言:javascript
复制
pFile = fopen("text.txt", "w");//当前
pFile = fopen(".//..//text.txt", "w");//上一路径
pFile = fopen(".//..//..//text.txt", "w");//前一路径
  1. 绝对路径 这个比较好理解,就是它的完整路径 比如
在这里插入图片描述
在这里插入图片描述

C:\Users\lenovo\Desktop就是它的绝对路径 操作为 这里还是把/改为//来防止转义字符

代码语言:javascript
复制
pFile = fopen("C:\\Users\\lenovo\\Desktop", "w");
(2) fclose

有打开就会有关闭,关闭文件函数为fclose

参数:指向指定要关闭的流的 FILE 对象的指针。 功能:关闭文件 关闭与流关联的文件并取消关联。与流关联的所有内部缓冲区都将与流取消关联并刷新:写入任何未写入输出缓冲区的内容,并丢弃任何未读输入缓冲区的内容。 返回值: 如果成功关闭流,则返回零值。失败 时,返回 EOF。

代码

代码语言:javascript
复制
    fclose(pf);//关闭
	pf = NULL;//为空指针,避免成为野指针

完整代码

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	int a= 10000;
	FILE* pf = fopen("tes.txt", "wb");//打开文件
	//操作
	//关闭文件
	fclose(pf);
	pf = NULL;//为空指针,避免成为野指针
	return 0;
}

四、文件的顺序读写

函数名

功能

适用于

fgetc

字符输入函数

所有输入流

fputc

字符输出函数

所有输出流

fgets

文本行输入函数

所有输入流

fputs

文本行输出函数

所有输出流

fscanf

格式化输入函数

所有输入流

fprintf

格式化输出函数

所有输出流

fread

二进制输入

文件输入流

fwrite

二进制输出

文件输出流

上面说的适用于所有输入流一般指适用于标准输入流(键盘)和其他输入流(如其他输入流);所有输出流一般指适用于标准输出流(屏幕/显示器)或其他输出流(如文件输出流)。

1、fputc

在这里插入图片描述
在这里插入图片描述

就是将一个字符以ASCll码值(或对应编码值)以2进制的形式存储到文件中 代码如下,如果想把26个字母全部存储,可以用循环操作

代码语言:javascript
复制
#include<stdio.h>

//打开文件
int main()
{
	FILE* pf = fopen("haha.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		pf = NULL;
	}
	//写文件
	fputc('a', pf);
	fputc('b', pf);
	fputc('c', pf);
	fputc('d', pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

我们也可以利用我们前面讲的标准流stdout直接输出

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	fputc('a', stdout);
	fputc('b', stdout);
	fputc('c', stdout);
	fputc('d', stdout);
	return 0;
}

结果

在这里插入图片描述
在这里插入图片描述

而这样以stdout直接输出到屏幕上的话,fputc这个函数就与putchar功能一样啦 代码如下:

代码语言:javascript
复制
#include<stdio.h>
int main()
{

    fputc('a', stdout);
	fputc('b', stdout);
	fputc('c', stdout);
	fputc('d', stdout);
	putchar('a');
	putchar('b');
	putchar('c');
	putchar('d');
	return 0;
}

结果如下:的确一样

在这里插入图片描述
在这里插入图片描述

2、fgetc

在这里插入图片描述
在这里插入图片描述

简单来说就是读取文件中的一个字符,我们前面已经向文件中写了abcd,我们现在进行读取,因为一次只能读取1个字符,所以我们用循环来读取 只读一个字符

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	FILE* pf = fopen("haha.txt", "r");
	if (pf == NULL)
	{
		perror("fopen"); // 打开失败时打印错误信息
		return 1; // 直接退出,避免后续错误操作
	}

	// 读文件(此时pf一定非NULL,无需再次判断)
	int ch = fgetc(pf);
	printf("%c", ch);

	// 关闭文件(必须在pf有效时调用)
	fclose(pf);
	pf = NULL; // 关闭后置空,避免野指针

	return 0;
}

用循环读取完,因为读取1个字符光标会移向下一个

代码语言:javascript
复制
#include<stdio.h>

int main()
{
	FILE* pf = fopen("haha.txt", "r");
	if (pf == NULL)
	{
		perror("fopen"); // 打开失败,打印错误信息
		return 1; // 直接退出,无需后续操作
	}
	
	// 读文件(此时pf一定非NULL)
	int ch;
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c", ch);
	}
	
	// 关闭文件(必须在文件操作完成后,且pf有效时调用)
	fclose(pf);
	pf = NULL; // 置空避免野指针
	
	return 0;
}

结果

在这里插入图片描述
在这里插入图片描述
二者结合

我们可以把1和2中结合一下,fgetc读取,fputc输出

代码语言:javascript
复制
#include <stdio.h>
int main() 
{
    int ch;
    while ((ch = fgetc(stdin)) != EOF) 
    { // 从键盘(stdin)读1字符
        fputc(ch, stdout); // 立即输出到屏幕(stdout)
    }
    return 0;
}

3、fputs

在这里插入图片描述
在这里插入图片描述

这个与fputc的区别就是fputc只能存储1个字符,而fputs可以直接把字符串存储到文件中 代码如下:

代码语言:javascript
复制
#include<stdio.h>
//打开文件
int main()
{
	FILE* pf = fopen("haha.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		pf = NULL;
	}
	//写文件
	fputs("abcd", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

同理,用stdout可以打印在屏幕上

代码语言:javascript
复制
#include<stdio.h>
int main()
{
	fputs("abcd", stdout);
	return 0;
}

结果

在这里插入图片描述
在这里插入图片描述

4、fgets

这个是我最常用的函数,可以直接读取一行,不用担心会因为空格跳过

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
>char * fgets ( char * str, int num, FILE * stream );

就是把stream中的num-1个元素,放在str中 这个函数就是将stream中读取num-1个元素和一个\0到str中

我们来使用它来将文件中的字母转换到数组中

代码语言:javascript
复制
#include<stdio.h>
int main()
{//打开文件
	FILE* pf = fopen("haha.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		pf = NULL;
	}
	//写文件
	char arr[20] = "xxxxxxxxxxxxxx";
	fgets(arr, 10, pf);
	printf("%s", arr);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

我们从下图可看出来的确会读取num-1个元素和/0,但是注意如果文件中含有/n且缓存区足够长时会读到换行符\n

在这里插入图片描述
在这里插入图片描述

我们可以把fgets应用到我们平常输入字符串中

代码语言:javascript
复制
#include<stdio.h>
#include<string.h>
int main()
{
	char arr[1000];
	fgets(arr, 100, stdin);
	arr[strcspn(arr, "\n")] = '\0';
	int num;
	scanf("%d", &num);
	printf("%s", arr);
	return 0;
}

其中 arr[strcspn(arr, “\n”)] = ‘\0’;是防止fgets最后\n直接影响到下一个读取,比如如果没有会影响num读取直接读取\n使读取失败,也可以用getchar()吸收也可以

在这里插入图片描述
在这里插入图片描述

我们发现如果这样不输出,原因是fgets会读取一行,所以我们输入后回车,就可以啦

在这里插入图片描述
在这里插入图片描述

5、fscanf

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

功能

核心是 格式化读取数据,与 fprintf 成对使用,按指定格式从输入流(键盘/文件)解析并读取多类型数据(int、float、字符串等),支持自定义分隔符,无需手动处理数据格式转换,适用于需按固定格式读取数据的场景(如配置文件、结构化文本)。

参数

函数原型:

代码语言:javascript
复制
int fscanf(FILE *stream, const char *format, ...);

stream指向FILE对象的指针,表示要读取的文件流(如stdin 、文件指针等) format :格式化字符串,定义如何解析输入数据(如%d 、 %f 、 %s 等)。 ... :可变参数列表,提供存储数据的变量地址(需与格式字符串中的说明符匹配)。

下面我们以代码展示 读取文件

代码语言:javascript
复制
#include<stdio.h>
 
struct Stu
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct Stu s = { 0 };
	FILE* pf = fopen("haha.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	fscanf(pf,"%s %d %f", s.name, &(s.age), &(s.score));//如果想多次取可用while,因为我前面只存储了字符,所以只能读取字符,后面全为0
////读文件
	//while (fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score)) != EOF)
	//{
	//	printf("%s %d %f\n", s.name, s.age, s.score);
	//}
	printf("%s %d %f\n",s.name,s.age, s.score);
	//关闭文件
	fclose(pf);
	pf = NULL;
 
	return 0;
}

因为我前面只存储了字符abcd,所以只能读取字符,后面全为0

在这里插入图片描述
在这里插入图片描述

读取键盘输入

代码语言:javascript
复制
#include <stdio.h>
int main() 
 {
    int num;
    char str[50];
    float score;
    // 按 "整数 字符串 浮点数" 格式读取键盘输入
    printf("输入格式:数字 字符串 成绩(如 10 小明 95.5):\n");
    fscanf(stdin, "%d %s %f", &num, str, &score); // 变量地址:int/float加&,数组名本身是地址
    printf("结果:编号=%d,姓名=%s,成绩=%.1f\n", num, str, score);
    return 0;
}

6、fprintf

加粗样式
加粗样式
在这里插入图片描述
在这里插入图片描述

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

fprintf是将格式化数据写入指定文件流的函数,和fscanf成对使用,核心是按照指定格式(如%d %s %f)将数据写入目标(文件/屏幕),对应fprintf(文件指针,格式控制符,输入数据) 我们可以发现这个参数前两个与fscanf一样,最后一个就是变量这里就不再讲了, 我们可以用它来写进文件 代码如下

代码语言:javascript
复制
#include<stdio.h>
struct Stu
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct Stu s = { "hh",20,80.5f };
	FILE* pf = fopen("haha.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fprintf(pf, "%s %d %f", s.name, s.age, s.score);
	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

如果在进行完这个程序再用fscanf进行读取,就可以看出效果啦

在这里插入图片描述
在这里插入图片描述

同理,printf第一个参数改为stdout后fprintf(stdout, , ) 效果与printf效果一样

7、fwrite

在这里插入图片描述
在这里插入图片描述

功能: fwrite是2进制写入函数,核心将“数据块”批量写入(如结构体、数组),不做格式转换,效率比fprintf(文本格式化)高 参数

代码语言:javascript
复制
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream )

ptr:指向要写入的数据块的地址; size:要写入的每个数据项的大小(以字节为单位); count:要写入的数据项的数量; stream:指向FILE类型结构体的指针,指向了要写入数据的文件流; 返回值 返回成功写入的元素个数

if返回值 =count——正确写入 <count——写入出错(磁盘空间不足、文件权限不足) =0——要么数据地址为NULL,要么文件指针无效

我们这里写一个结构体数组到二进制文件中 代码如下:

代码语言:javascript
复制
#include<stdio.h>
struct Student 
{ int id; 
char name[20]; 
float score;
}; // 自定义结构体

int main() 
{
    FILE* fp = fopen("student.bin", "wb"); // 二进制写模式(wb)
    struct Student stu[2] = { {1, "张三", 88.5}, {2, "李四", 92.0} };

    if (!fp) 
        return 1;
    // 写入2个Student结构体,每个大小sizeof(struct Student)
    fwrite(stu, sizeof(struct Student), 2, fp);

    fclose(fp);
    fp = NULL;
    return 0;
}

文件以2进制形式打开

在这里插入图片描述
在这里插入图片描述

8、fread

在这里插入图片描述
在这里插入图片描述

功能: 核心是 二进制块读取,与 fwrite 成对使用,按指定“数据块大小+个数”批量读取二进制文件(如结构体、数组),不做格式转换,直接将文件原始字节数据存入指定缓冲区,效率高于格式化读取(如 fscanf ),适用于批量结构化数据(数组、结构体)的读取。 参数

代码语言:javascript
复制
size_t fread(void* ptr, size_t size, size_t count, FILE* stream);

ptr:接收读取数据的首元素的地址(存储数据的目标位置); size:单一元素的字节数 count:读取的元素总数(数据块个数) stream:指向FILE类型结构体的指针,指定了要从中读取数据的文件流。(注意:要以2进制打开(rb)); 返回值 返回成功读取的元素个数

if返回值 1、<count——读到文件末尾(EOF)或读取时出错(如文件损坏) 2、=count——正常读取 3、=0——要么已到达文件末尾,要么缓冲区地址为NULL或文件指针无效

我们前一个函数fwrite已经已2进制存储啦,现在来读取 代码如下

代码语言:javascript
复制
#include <stdio.h>
struct Student 
{ int id; 
char name[20];
float score; 
};

int main() 
{
    FILE* fp = fopen("student.bin", "rb"); // 二进制读模式(rb)
    struct Student stu[2]; // 存储读取的数据
    size_t readNum; // 接收成功读取的元素个数

    if (!fp) 
        return 1;
    // 读取2个Student结构体,存入stu数组
    readNum = fread(stu, sizeof(struct Student), 2, fp);

    // 验证读取结果
    printf("成功读取 %zu 个学生数据:\n", readNum);
    for (int i = 0; i < readNum; i++)
    {
        printf("ID:%d,姓名:%s,成绩:%.1f\n", stu[i].id, stu[i].name, stu[i].score);
    }

    fclose(fp);
    return 0;
}

结果如下,和我们输入的一样

在这里插入图片描述
在这里插入图片描述

五、格式化与不格式化区别

上面讲了有些函数存储时格式化或不格式化,担心大家不懂,现在列个表格进行对比 格式化与不格式化数据场景对比表

对比维度

格式化数据

不格式化数据(原生数据)

核心特征

遵循固定规则/结构存储/传输

以原生字节流形式存储/传输,无预设结构

数据结构

有明确结构(如键值对、字段分隔、格式符约束)

无统一结构,仅为连续字节/纯文本流

人类可读性

高(直接看懂内容,如表格、JSON文本)

低(二进制需解析,纯文本无分隔也难理解)

程序解析难度

低(按固定格式直接解析,无需额外逻辑)

高(需自定义解析规则,如二进制文件解析协议)

典型场景

配置文件、接口数据交换、日志记录、用户输入输出

媒体文件(图片/视频)、二进制文件存储、高效数据传输

常见示例

JSON文件、CSV表格、printf("%d", num)输出、数据库表

图片(.png/.jpg)、视频(.mp4)、二进制日志、fread/fwrite读写的文件

优点

易调试、跨平台/跨程序兼容、开发效率高

存储/传输效率高(占用空间小、速度快)、保留原始数据细节

缺点

额外占用少量存储空间、传输有格式解析开销

可读性差、调试困难、跨程序需统一解析规则


六、对比一组函数

我们接下来来对比

scanf/fscanf/sscanf printf/fprintf/sprintf

我们先来认识一下sscanf和sprintf

1、sscanf

在这里插入图片描述
在这里插入图片描述

功能: **从指定字符串中按格式化规则读取数据,并存储到对应变量中。**常用于解析字符串中的结构化数据(如从配置字符串提取参数、解析协议字符串等场景)。 参数

代码语言:javascript
复制
int sscanf ( const char * s, const char * format, ...);

const char *str:待解析的源字符串(const修饰,保证字符串不被修改)。 const char *format:格式控制字符串(与scanf格式符一致,如%d、%s、%f等)。 可变参数列表:接收数据的变量地址(如&num、&str等),需与format中的格式符一一对应。

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

int main() {
    // 待解析的源字符串(包含姓名和年龄)
    char str[] = "name:Alice age:22";
    char name[20];
    int age;

    // 用sscanf按格式提取数据
    sscanf(str, "name:%s age:%d", name, &age);

    // 打印结果
    printf("姓名:%s,年龄:%d\n", name, age);
    return 0;
}

结果

在这里插入图片描述
在这里插入图片描述

2、sprintf

在这里插入图片描述
在这里插入图片描述
  • 功能:将各种类型的数据按格式化规则拼接成一个字符串,存储到指定的字符数组中。常用于构造格式化日志、拼接协议字符串、生成带格式的文本等场景。
  • 参数
    • char *str:目标字符数组(需保证足够大的空间,否则易引发缓冲区溢出风险)。
    • const char *format:格式控制字符串(与printf格式符一致)。
    • 可变参数列表:待格式化的数据(如整数、浮点数、字符串等),需与format中的格式符一一对应。
代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

int main() {
    char result[100]; // 存储结果的字符数组
    int id = 1001;
    float score = 98.5;
    // 用sprintf将id和score格式化为字符串
    sprintf(result, "学生学号:%d,期末成绩:%.1f", id, score);
    // 打印拼接后的字符串
    printf("%s\n", result);
    return 0;
}

结果

在这里插入图片描述
在这里插入图片描述

3、对比

我们可以发现这些函数都十分像,这里我用几个图来简单表示区别

在这里插入图片描述
在这里插入图片描述

函数组

函数名

功能

适用场景

输入函数组

scanf

格式化标准输入读取

从键盘(标准输入)读取

fscanf

格式化文件输入读取

从文件输入流读取

sscanf

格式化字符串输入读取

从字符串中读取

输出函数组

printf

格式化标准输出打印

输出到屏幕(标准输出)

fprintf

格式化文件输出打印

输出到文件输出流

sprintf

格式化字符串输出打印

输出到字符串中

结束语

由于字数原因,博主把文件操作分成两篇进行发布,下一篇我们将会讲到文件的随机读写和结束判定及缓冲区,本篇到这里就结束啦,本篇的函数不仅用在文件操作中,有的还可以对字符串的读取及转换等有帮助,相信大家都有所收获,有什么问题欢迎大家来评论区进行讨论呀,感谢大家的支持啦ヽ(≧∀≦)ノ!我们下一篇见!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、文件使用的原因及目的
    • 1、何为文件
    • 2、为什么使用文件
    • 3、文件的种类
      • (1)程序文件
      • (2)数据文件
    • 4、文件名
  • 二、二进制文件和文本文件
    • 数据在文件中的存储方式
  • 三、文件的打开和关闭
    • 1、流和标准流
      • (1) 流
      • (2) 标准流
    • 2、文件指针
    • 3、文件的打开和关闭
      • (1) fopen
      • 路径
      • (2) fclose
  • 四、文件的顺序读写
    • 1、fputc
    • 2、fgetc
      • 二者结合
    • 3、fputs
    • 4、fgets
    • 5、fscanf
    • 6、fprintf
    • 7、fwrite
    • 8、fread
    • 五、格式化与不格式化区别
    • 六、对比一组函数
    • 1、sscanf
    • 2、sprintf
    • 3、对比
  • 结束语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档