C语言教程——文件处理(2)

news/2025/2/1 19:25:53 标签: c语言, 开发语言

目录

前言

一、顺序读写函数(续)

1.1fprintf

1.2fscanf

1.3fwrite

1.4fread

二、流和标准流

2.1流

2.2标准流

2.3示例

三、sscanf和sprintf

3.1sprintf

3.2sscanf

四、文件的随机读写

4.1fseek

4.2ftell

4.3rewind

五、文件读取结束的判定

5.1feof相关判断知识

六、文件缓冲区

总结



前言

昨天最顺序读写函数了解了四个,今天接着学习。


一、顺序读写函数(续)

1.1fprintf

格式化输出函数

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

第一个参数就是文件,第二个参数就是与printf的后面的参数一样。

我们可以用代码来掩饰一下:

我们先定义一个结构体变量:

struct S {
	char name[20];
	int age;
	int Id;
};

之后调用fprintf函数写入文件

struct S s = { "zhangsan",14,12345 };
fprintf(df,"%s %d %d\n",s.name,s.age,s.Id);

同样还是作用于之前的目标文件。

我们打开文件就可以看见,成功的运行了。

1.2fscanf

这里就是格式化读写文件

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

通过fscanf就可以进行访问,一样的我们把创建一个结构体来接收:

FILE* df = fopen("D:\\project\\text.txt", "r");
struct S s = {0};
fscanf(df, "%s %d %d", s.name, &(s.age), &(s.Id));
printf("%s %d %d\n", s.name, s.age, s.Id);

之后就可以通过fscanf访问之前存入的数据,我们可以打印出来,然后运行一下看看:

我们可以看见,刚才访问的数据就实现了。

1.3fwrite

接着是二进制出入文件函数

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

第一个参数就是要传入的原始数据指针(因为不知道是什么类型,所以之类用void*类型表示),第二个是这个原始数据的大小,第三个是要传入文件的个数,最后一个就是要传入的文件。

我们可以用代码来演示:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

struct S {
	char name[20];
	int age;
	float score;
};

int main()
{
	struct S s = { "李白",34,99.9 };
	//打开文件
	FILE* df = fopen("D:\\project\\text.txt", "wb");
	if (df == NULL)
	{
		perror("fopen::");
		return 1;
	}
	//二进制写入
	fwrite(&s, sizeof(struct S), 1, df);
	//关闭文件
	fclose(df);
	df = NULL;
	return 0;
}

注意这里的方式改用了wb,因为它是为了输出数据,打开⼀个⼆进制⽂件,这里把结构体中的数据变为了二进制进行写入文件,我们可以打开文件看看:

这里发现变成了一堆不认识的东西,如果我们用二进制打开就可以看到:

这是一堆二进制,如果对二进制进行分析,就会得到一些数据。

1.4fread

二进制读取函数

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

我们发现这里的参数和二进制写入是一模一样的,所以话不多说,我们可以直接写入代码。

这里打开文件的方式改为‘rb’。

在代码之前我们可以看一下txt文件里是什么:

我们还不认识,接下来用fread来读取后输出,看看打印出来的是什么。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

struct S {
	char name[20];
	int age;
	float score;
};

int main()
{
	struct S s = {0};
	//打开文件
	FILE* df = fopen("D:\\project\\text.txt", "rb");
	if (df == NULL)
	{
		perror("fopen::");
		return 1;
	}
	//二进制读取
	fread(&s, sizeof(struct S), 1, df);
	printf("%s %d %f\n", s.name, s.age, s.score);
	//关闭文件
	fclose(df);
	df = NULL;
	return 0;
}

我们运行后:

发现是我们之前的数据,这里第三个数不一样的原因是因为我们使用的是浮点数进行输出,所以这里精度会出现偏差。

二、流和标准流

我们前面可以看到有流的概念,所以在这里再说一下流的概念。

2.1流

我们程序的数据需要输入到各种外部设备,也需要从外部设备中获得数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念,我们可以把流想成一条河,用的时候从里面拿,不用的时候可以还回去。

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

2.2标准流

C程序在启动的时候,默认启动了三个流,分别是:

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

stdout 标准输出流,大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出流中。

stderr 标准错误流,大多数环境中输出到显示器界面。

这是默认打开的三个流,我们使用scanf,printf等函数就可以直接进行输入输出函数的。

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

我们可以通过文件函数来访问流从而输入输出:

2.3示例

这里通过直接调用stdin从输入流读数据,完了显示到输出流中。

struct S {
	char name[20];
	int age;
	float score;
};

int main()
{
	struct S s = { 0 };
	fscanf(stdin, "%s %d %f", s.name,&(s.age), &(s.score));
	fprintf(stdout, "%s %d %f\n", s.name, s.age, s.score);
	return 0;
}

运行一下我们可以看到:

这里实现了读取和输出。

我们也可以用fputc和fgetc来掩饰,十分的简单易懂:

int main()
{
	
	int c=fgetc(stdin);
	fputc(c, stdout);
	return 0;

}

运行结果就是:

输入一个字符,输出一个字符

三、sscanf和sprintf

3.1sprintf

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

这个函数可以把后面的类型成员放到前面的字符串中。

返回值:成功后,将返回写入的字符总数。此计数不包括自动追加在字符串末尾的其他 null 字符。
失败时,将返回负数。

我们可以通过代码来理解:

struct S {
	char name[20];
	int age;
	float score;
};

首先我们先定义了一个结构体,然后通过后续操作把结构体中的成员按照原本的类型格式放到字符串中:

int main()
{
	struct S s = { "张三",39,99.99 };
	char arr[100] = { 0 };
	sprintf(arr, "%s %d %f", s.name, s.age, s.score);
	printf("%s\n", arr);
	return 0;
}

我们运行之后就可以看见:

确实按照原本的格式放到了arr字符串中。

3.2sscanf

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

str是要读取的字符串,format是格式化字符串,...是根据格式化字符串提供的格式来指定要解析的数据类型和要存放数据的变量。

sscanf函数根据格式化字符串的格式,从字符串中提取数据,并将数据存入对应的变量中。它可以用来解析字符串中的数字、字符、字符串等数据,并将其存入变量中。

所以我们可以通过代码来实现一下,把上面字符串中的数据提取出来,然后放到一个新的结构体中:

int main()
{
	struct S s = { "张三",39,99.99 };
	char arr[100] = { 0 };
	sprintf(arr, "%s %d %f", s.name, s.age, s.score);
	printf("%s\n", arr);

	struct S s1 = {0};
	sscanf(arr, "%s %d %f", s1.name, &(s1.age), &(s1.score));
	printf("%s %d %f", s1.name, s1.age, s1.score);

	return 0;
}

这就实现了从字符串中提取,然后放到新的结构体中,我们打印新的结构体中的数据,就可以看见:

实现了新结构体的打印。

四、文件的随机读写

4.1fseek

根据文件指针的位置和偏移量来定位文件指针。

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

这里第一个参数就是那个要访问的流,第二个是偏移量,第三个是选哪种方式,这里有三种方式。

ConstantReference position
SEEK_SETBeginning of file(文件开头)
SEEK_CURCurrent position of the file pointer(当前位置)
SEEK_ENDEnd of file *(文件结尾)

我们这里还用之前的文件,里面给上:

代码文件打开时,是指在a前面的。就可以通过代码来演示:

int main()
{
	FILE* df = fopen("D:\\project\\text.txt", "r");
	int ch = 0;
	ch=fgetc(df);
	printf("%c\n", ch);
	ch = fgetc(df);
	printf("%c\n", ch);
	ch = fgetc(df);
	printf("%c\n", ch);
}

这里通过fgetc来获取当前字符,函数使用一次那么指针就往后走一个。这时候运行就是:

我们如果想返回去访问之前的字符,那么就可以用fseek来实现,因为此时是在d前面,我们如果要访问下一个字符为b,如果定义的是当前位置,那么指针就需要往前2个位置,偏移量也就是-2:

printf("%c\n", ch);
fseek(df, -2, SEEK_CUR);
ch = fgetc(df);
printf("%c\n", ch);

我们通过运行就可以看到:

这就访问到了b。除了这一种方法还可以用另外两种,相对于开始b的偏移量就是1,相对于末尾b的偏移量就是-3,这样对参数进行赋值,那么就可以成功实现访问b。

但是如果我们不知道偏移量为多少,那么就可以用下面这个函数。

4.2ftell

返回当前指针位置

long int ftell ( FILE * stream );

传入文件指针,那么返回的就是一个整形,这个整形就是文件指针当前指向的位置。 

这里针对之前的代码:

printf("%d\n", ftell(df));

结果显示的就是2. 

4.3rewind

将指针返回到开始,起始位置。

void rewind ( FILE * stream );

这里也是针对之前的代码: 

rewind(df);
printf("%d\n", ftell(df));

 结果就是0.

还有很多的函数,感兴趣的可以自己看看。

五、文件读取结束的判定

5.1feof相关判断知识

在文件读取的过程中,不能使用feof函数的返回值直接用来判断文件的是否结束。而是应用于当文件结束的时候,判断文件是否遇到文件结尾结束。

1.文本文件读取是否结束:

fgetc判断结束是否为EOF,返回失败返回EOF

fgets判断结束是否为NULL,返回失败是返回一个空指针

2.二进制文件的读取结束判断:

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

文件读取结束了,结束后想知道结束的原因:

feof返回为真的话,就说明是文件正常读取到了结束标志而结束的。

ferror返回为真的话,就说明文件在读取过程中出错了而结束的。

六、文件缓冲区

文件缓冲区是在计算机系统中用来临时存放文件数据的一块内存区域。当计算机需要读取或写入文件数据时,通常会先将数据读取到文件缓冲区中,然后再根据需要将数据从缓冲区移动到内存或磁盘中。

文件缓冲区的存在可以提高文件读写的效率。由于磁盘操作相对较慢,每次读写都需要进行磁盘寻址,而将数据读取到缓冲区中可以避免频繁的磁盘操作。当数据写入缓冲区时,系统可以选择将数据缓存一段时间后再进行实际的写入操作,从而避免频繁的磁盘写入。

文件缓冲区一般由操作系统提供,可以是内核级别的缓冲区或用户级别的缓冲区。内核级别的缓冲区由操作系统管理,对于用户程序来说是透明的;而用户级别的缓冲区由程序员自己管理,可以根据需要进行灵活的控制。

使用文件缓冲区需要注意及时刷新缓冲区和关闭文件。当数据写入缓冲区后,如果不及时刷新缓冲区,数据可能不会立即写入磁盘中;而关闭文件时,系统会自动将缓冲区中的数据写入磁盘。

总之,文件缓冲区是一种提高文件读写效率的技术,可以有效减少磁盘操作次数,提高系统性能。

ANSIC 标准采⽤“缓冲⽂件系统” 处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的⼤⼩根据C编译系统决定的。

因为有缓冲区的存在,C语⾔在操作⽂件的时候,需要做刷新缓冲区或者在⽂件操作结束的时候关闭⽂件


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。


http://www.niftyadmin.cn/n/5839535.html

相关文章

DeepSeek R1功能设计涉及的几个关键词

DeepSeek R1作为人工智能助手&#xff0c;其功能设计主要基于以下步骤&#xff1a; 字典过滤与词汇选择 使用蒸馏技术对候选词汇进行筛选和优化&#xff0c;确保选择的词汇与上下文语境相关且准确。候选词汇通过多源数据&#xff08;如公开文档、专家分析等&#xff09;进行训练…

jQuery小游戏(二)

jQuery小游戏&#xff08;二&#xff09; 今天是新年的第二天&#xff0c;本人在这里祝大家&#xff0c;新年快乐&#xff0c;万事胜意&#x1f495; 紧接jQuery小游戏&#xff08;一&#xff09;的内容&#xff0c;我们开始继续往下咯&#x1f61c; 游戏中使用到的方法 key…

AnyThingLLM本地私有知识库搭建

***************************************************** 环境准备 操作系统&#xff1a;Windows11 内存&#xff1a;32GB RAM 存储&#xff1a;预留 300GB 可用空间 显存: 16G 网络: 100M带宽 前置准备: 已安装ollama环境 deepseek本地大模型 ***************************…

第六章 窗口管理

HarmonyOS通过窗口模块实现在同一块物理屏幕上提供多个应用界面显示和交互。 6.1 窗口开发概述 HarmonyOS通过窗口模块实现窗口管理&#xff0c;包括&#xff1a; 针对应用开发者&#xff0c;提供了界面显示和交互能力。 针对终端用户&#xff0c;提供了控制应用界面的方式。…

第05章 17 Contour 过滤器介绍与例子

vtkContourFilter 是 VTK&#xff08;Visualization Toolkit&#xff09;中的一个关键类&#xff0c;用于从输入数据生成等值线或等值面。它是基于阈值的过滤器&#xff0c;可以从标量字段中提取等值线或等值面。vtkContourFilter 的核心功能是根据用户指定的值生成等值线或等值…

【玩转全栈】--创建一个自己的vue项目

目录 vue介绍 创建vue项目 vue页面介绍 element-plus组件库 启动项目 vue介绍 Vue.js 是一款轻量级、易于上手的前端 JavaScript 框架&#xff0c;旨在简化用户界面的开发。它采用了响应式数据绑定和组件化的设计理念&#xff0c;使得开发者可以通过声明式的方式轻松管理数据和…

前端力扣刷题 | 4:hot100之 子串

560. 和为K的子数组 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 示例&#xff1a; 输入&#xff1a;nums [1,1,1], k 2 输出&#xff1a;2 法一&#xff1a;暴力法 var subar…

android安卓用Rime

之前 [1] 在 iOS 配置用上自改方案 [2]&#xff0c;现想在安卓也用上。Rime 主页推荐了两个安卓平台支持 rime 的输入法 [3]&#xff1a; 同文 Tongwen Rime Input Method Editor&#xff0c;但在我的 Realme X2 Pro 上似乎有 bug&#xff1a;弹出的虚拟键盘只有几个 switcher…