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

embedded/2025/2/1 23:28:14/

目录

前言

一、顺序读写函数(续)

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.ppmy.cn/embedded/158752.html

相关文章

STM32 ADC

stm32单片机- ADC-技术详细解程序示范&#xff08;FREERTOSHAL多通道DMA&#xff09; - 知乎 (zhihu.com) 记录自己的嵌入式学习之路-CSDN博客 【STM32】ADC_stm32 adc-CSDN博客 STM32——ADC篇&#xff08;ADC的使用&#xff09;_stm32 adc-CSDN博客 【STM32 ADC】-CSDN博客…

使用Python和Qt6创建GUI应用程序--关于Qt的一点介绍

关于Qt的一点介绍 Qt是一个免费的开源部件工具包&#xff0c;用于创建跨平台GUI应用程序&#xff0c;允许应用程序从Windows瞄准多个平台&#xff0c;macOS&#xff0c; Linux和Android的单一代码库。但是Qt不仅仅是一个Widget工具箱和功能内置支持多媒体&#xff0c;数据库&am…

win11本地部署 DeepSeek-R1 大模型!免费开源,媲美OpenAI-o1能力,断网也能用

一、下载ollama 二、安装ollama 三、部署DeepSeek-R1 在cmd窗口中先输入ollama -v查看ollama是否安装成功&#xff0c;然后直接运行部署deepseek-r1的命令 ollama run deepseek-r1&#xff0c;出现下面界面即为安装成功。 C:\Users\admin>ollama -v ollama version is 0.5…

DeepSeek的崛起与全球科技市场的震荡

引言 近年来&#xff0c;人工智能&#xff08;AI&#xff09;技术的快速发展不断重塑全球科技格局。 近日&#xff0c;中国初创企业DeepSeek推出了一款据称成本极低且性能强大的AI模型&#xff0c;引发全球市场的剧烈反应。NVIDIA、台积电等半导体和AI科技巨头股价大幅下跌&am…

Python练习(3)

今日题单 刮刮彩票字母串胎压监测别再来这么多猫娘了&#xff01;吃火锅前世档案统计数字字符和空格随机输一次 代码示例 刮刮彩票 price [10000, 36, 720, 360, 80, 252, 108, 72, 54, 180, 72, 180, 119, 36, 306, 1080, 144, 1800, 3600] lst [] x 0 for i in range(…

VUE之组件通信(一)

1、props 概述&#xff1a;props是使用频率最高的一种通信方式&#xff0c;常用与&#xff1a;父<——>子。 若 父传子&#xff1a;属性值是非函数。若 子传父&#xff1a;属性值是函数。 父组件&#xff1a; <template><div class"father">&l…

Linux《基础指令》

在之前的Linux《Linux简介与环境的搭建》当中我们已经初步了解了Linux的由来和如何搭建Linux环境&#xff0c;那么接下来在本篇当中我们就要来学习Linux的基础指令。在此我们的学习是包括两个部分&#xff0c;即指令和关于Linux的基础知识&#xff1b;因此本篇指令和基础知识的…

如何跨互联网adb连接到远程手机-蓝牙电话集中维护

如何跨互联网adb连接到远程手机-蓝牙电话集中维护 --ADB连接专题 一、前言 随便找一个手机&#xff0c;安装一个App并简单设置一下&#xff0c;就可以跨互联网的ADB连接到这个手机&#xff0c;从而远程操控这个手机做各种操作。你敢相信吗&#xff1f;而这正是本篇想要描述的…