在操作系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。我们不去探讨硬件设备是如何被映射成文件的,把任意 I/O 设备,转换成逻辑意义上的标准 I/O 设备或标准文件的过程,并不需要程序设计者感知和处理,是由标准 I/O 系统自动转换完成的。大家只需要记住,在C语言中硬件设备可以看成文件,有些输入输出函数不需要你指明到底读写哪个文件,系统已经为它们设置了默认的文件,我们对于文件的操作就相当于对硬件设备进行操作,在本篇博客,详细总结理解以及对如何进行文件的操作,达到活学活用。
一、为什么使用文件?
程序运行时,数据是从内存获取的,同时计算好的结果也保存在内存中,内存中的数据有个明显的缺点就是:断电数据便消失,只有当程序重新运行,才会加载数据,这并不适用实际的开发。在进行ATM机的项目时,ATM机的程序运行起来的时候,可以进行各种增删改查数据操作,此时数据是存放在内存中,当程序退出的时候,当中的数据自然就不存在了,等下次运行程序的时候,数据又得重新录入,如果这样就很难受。 我们在想既然是ATM机就应该把信息记录下来,只有我们自己选择删除数据的时候,数据才不复存在。 这就涉及到了数据持久化的问题,
我们一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据库等方式。 使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。
二、文件基础
2.1 什么是文件,如何理解
所谓文件(file)一般指存储在外部介质上数据的集合,比如我们经常使用的txt、bmp 、jpg、exe、rmvb等等。这些文件各有各的用途,我们通常将它们存放在磁盘或者可移动盘等介质中。 文件无非就是一段数据的集合,这些数据可以是有规则的集合,也可以是无序的集合。操作系统也就是以文件为单位对数据进行管理的。也就是说,要访问外部介质上的数据,必须先按照文件名进行查找,然后从该文件中读取数据。要想写数据到外部介质,必须得建立一个文件,然后再写入。因此,你眼前的文件只是数据的集合。 常见的文件如下:
为了简化各种设备的操作,使用户不必区分各种设备之间的区别,操作系统 把各种设备都 统一作为文件来处理。即不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。
从操作系统的角度看,每一个输入输出设备都是一个文件。其中键盘是标准输入设备,其文件标识符为”stdin”,屏幕是标准输出设备,其文件标识符为”stdout”。
2.2 文件的分类
在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。
- 程序文件:包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境 后缀为.exe)。
- 数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件, 或者输出内容的文件。 本章讨论的是数据文件。
在以前各章所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。 其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件。
不管是键盘、显示器还是其他硬件设备最终都被映射成磁盘上的一个个文件,我们对文件操作就可以理解为对于硬件设备或者数据文件进行操作!
2.3 文件组成三要素
一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件组成三要素:文件路径、文件名、后缀。
由于在 C 语言中 '\' 一般是转义字符的起始标志,故在路径中需要用两个 '\' 表示路径中目录层次的间隔,也可以使用 '/' 作为路径中的分隔符。 例如: "D:\\tulun\\test.c"或者"D:/tulun/test.c",表示文件 test.c 保存在 D 盘tulun目录下。 “tu.txt表示当前目录下的文件 tu.txt。前者是绝对路径,后者是相对路径。
2.4 绝对路径和相对路径
绝对路径
绝对路径就是你的主页上的文件或目录在硬盘上真正的路径。比如,你的main.txt文件 是存放在 d:/test/bin/下的,那么 d:/test/bin 就是main.txt文件的绝对路径。
相对路径
相对路径就是相对于当前文件的路径。网页中一般表示路径使用这个方法。 在网络中,以http开头的链接都是绝对路径,绝对路径一般在CGI程序的路径配置中经常用到,而在制作网页中实际很少用到。
相对路径使用的特殊符号
以下为建立路径所使用的几个特殊符号,及其所代表的意义。
- "./":代表目前所在的目录。
- "../":代表上一层目录。
- 以"/"开头:代表根目录。
三、流的概念,如何理解
I/O 设备的多样性及复杂性,给程序设计者访问这些设备带来了很大的难度和不便。 为此,ANSIC 的 I/O 系统即标准 I/O 系统,把任意输入的源端或任意输出的终端,都抽象 转换成了概念上的“标准 I/O 设备”或称“标准逻辑设备”。程序绕过具体设备,直接与 该“标准逻辑设备”进行交互,这样就为程序设计者提供了一个不依赖于任何具体 I/O 设 备的统一操作接口,通常把抽象出来的“标准逻辑设备”或“标准文件”称作“流”。
所有的文件(保存在磁盘)都要载入内存才能处理,所有的数据必须写入文件(磁盘)才不会丢失。数据在文件和内存之间传递的过程叫做文件流,类似水从一个地方流动到另一个地方。数据从文件复制到内存的过程叫做输入流,从内存保存到文件的过程叫做输出流。
文件是数据源的一种,除了文件,还有数据库、网络、键盘等;数据传递到内存也就是保存到C语言的变量(例如整数、字符串、数组、缓冲区等)。我们把数据在数据源和程序(内存)之间传递的过程叫做数据流(Data Stream)。相应的,数据从数据源到程序(内存)的过程叫做输入流(Input Stream),从程序(内存)到数据源的过程叫做输出流(Output Stream)。
输入输出(Input output,IO)是指程序(内存)与外部设备(键盘、显示器、磁盘、其他计算机等)进行交互的操作。几乎所有的程序都有输入与输出操作,如从键盘上读取数据,从本地或网络上的文件读取数据或写入数据等。通过输入和输出操作可以从外界接收信息,或者是把信息传递给外界。
我们可以说,打开文件就是打开了一个流。流是一种抽象的概念,负责在数据的产生者和数据的使用者之间建立联系,并管理数据的流动。如下图所示:数据从文件(外存)流向内存称为输入流,数据从内存流向文件(外存)称为输出流。
C语言程序,只要运行起来,就默认打开了3个流,因为这是由系统打开的,可直接使用:
- stdin- 标准输入流文件 - 键盘(从键盘获取数据,写入到内存)
- stdout - 标准输出流文件- 屏幕(将内存中计算的数据,输出到电脑的屏幕上)
- stderr - 标准错误流文件 - 屏幕 (将错误的信息数据,输出到电脑的屏幕上)
四、文件的打开和关闭
ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序 中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
操作文件的正确流程:
(1)打开文件:就是获取文件的有关信息,这些信息会被保存到一个FILE类型的结构体变量中,常用fopen()库函数实现对于文件的打开;
(2)读写文件:文件读写方式比较灵活,主要分为顺序读写和随机读写,这些同样有相应的库函数实现;
(3) 关闭文件:断开与文件的联系,释放结构体变量,同时禁止再对该文件进行操作,常用fclose()库函数实现对于文件的关闭;
注意:文件在进行读写操作之前一定要先打开,使用完毕要进行关闭!
4.1 文件指针
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。 每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。在stdio.h中定义的一个结构体,这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE。通过文件指针就可对它所指的文件进行各种操作。不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。 每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息, 使用者不必关心细节。vs2012 stdio.h中的FILE结构体如下:
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便,定义文件指针的一般形式为:
FILE* pf;//文件指针变量
定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件。
4.2文件的打开和关闭
4.2.1 文件的打开-fopen()函数
C语言中,操作文件之前必须先打开文件,所谓“打开文件”就是让程序和文件建立连接的过程,为文件建立相应的信息区(用来存放有关文件的信息)和文件缓冲区(用来暂时存放输入输出的数据)。打开文件之后,程序可以得到文件的相关信息,例如大小、类型、权限、创建者、更新时间等。在后续读写文件的过程中,程序还可以记录当前读写到了哪个位置,下次可以在此基础上继续操作。在编写程序时,在打开文件的同时,一般都指定一个指针变量(文件指针)指向该文件,也就是建立指针变量与文件之间的关系,这样通过该指针变量(文件指针)对文件进行读写。
fopen() 函数会获取文件信息,包括文件名、文件状态、当前读写位置等,并将这些信息保存到一个 FILE 类型的结构体变量中,然后将该变量的地址返回。FILE 是 <stdio.h> 头文件中的一个结构体,它专门用来保存文件信息。我们不用关心 FILE 的具体结构,只需要知道它的用法就行。
打开文件函数原型:FILE * fopen ( const char * filename, const char * mode );
(1)函数参数:
filename: 文件名,包括路径,如果不显式含有路径,则表示当前路径。例如,“D:\\text.txt”表示 D 盘根目录下的文件 text.txt 文件。“tulun.txt”表示当前目录下的文件 tulun.txt。
mode: 文件打开模式,指出对该文件可进行的操作。常见的打开模式如 “r” 表示只读,“w” 表示只写,“rw” 表示读写,“a” 表示追加写入。
两个参数都是字符串。
(2)返回值:打开成功,返回该文件对应的 FILE 类型的指针; 打开失败,返回 NULL。我们可以利用这一点来判断文件是否打开成功,通过判断 fopen() 的返回值是否和 NULL 相等来判断是否打开失败:如果 fopen() 的返回值为 NULL,那么 fp 的值也为 NULL,此时 if 的判断条件成立,表示文件打开失败。读者在打开文件时一定要判断文件是否打开成功,因为一旦打开失败,后续操作就都没法进行了,往往以“结束程序”告终。
不同的操作需要不同的文件权限。例如,只想读取文件中的数据的话,“只读”权限就够了;既想读取又想写入数据的话,“读写”权限就是必须的了。另外,文件也有不同的类型,按照数据的存储方式可以分为二进制文件和文本文件,它们的操作细节是不同的。
在调用 fopen() 函数时,这些信息都必须提供,称为“文件打开方式”。最基本的文件打开方式有以下几种:
整体来说,文件打开方式由 r、w、a、t、b、+ 六个字符拼成,各字符的含义是:
- r(read):读
- w(write):写
- a(append):追加
- t(text):文本文件
- b(binary):二进制文件
- +:读和写
FILE *fp = fopen("demo.txt", "r");
表示以“只读”方式打开当前目录下的 demo.txt 文件,
并使 fp 指向该文件,这样就可以通过 fp 来操作 demo.txt 了。fp 通常被称为文件指针。
4.2.2 文件的关闭-fclose()函数
文件一旦使用完毕,应该用 fclose() 函数把文件关闭,以释放相关资源,避免数据丢失。
关闭文件函数 fclose 的原型:int fclose ( FILE * stream );
(1)函数参数
strem : 指向要关闭流对象的指针。
(2)返回值:
如果流被成功关闭,返回0值。失败时返回EOF(-1)。 即使调用失败,作为参数传递的流将不再与文件或其缓冲区关联。
1.打开文件
FILE *fp = fopen("demo.txt", "r");
2.读写文件
3.关闭文件
fclose(fp);
五、文本文件和二进制文件
根据文件中数据的组织形式的不同,可以把文件分为:文本文件和二进制文件。一个数据在内存中是怎么存储的呢? 字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
- 文本文件:把要存储的数据当成一系列字符组成,把每个字符的 ASCII 码值存入文件中。每个 ASCII 码值占一个字节,每个字节表示一个字符。故文本文件也称作字符文件或 ASCII 文件,是字符序列文件。
- 二进制文件:把数据对应的二进制形式存储到文件中,是字节序列文件。
根据我们以往的经验,文本文件通常用来保存肉眼可见的字符,比如
.txt
文件、.c
文件、.dat
文件等,用文本编辑器打开这些文件,我们能够顺利看懂文件的内容。二进制文件通常用来保存视频、图片、程序等不可阅读的内容,用文本编辑器打开这些文件,会看到一堆乱码,根本看不懂。但是从物理上讲,二进制文件和字符文件并没有什么区别,它们都是以二进制的形式保存在磁盘上的数据。我们之所以能看懂文本文件的内容,是因为文本文件中采用的是 ASCII、UTF-8、GBK 等字符编码,文本编辑器可以识别出这些编码格式,并将编码值转换成字符展示出来。而二进制文件使用的是 mp4、gif、exe 等特殊编码格式,文本编辑器并不认识这些编码格式,只能按照字符编码格式胡乱解析,所以就成了一堆乱七八糟的字符,有的甚至都没见过。
如果我们新建一个 mp4 文件,给它写入一串字符,然后再用文本编辑器打开,你一样可以读得懂,有兴趣的读者可以自己试试。
总起来说,不同类型的文件有不同的编码格式,必须使用对应的程序(软件)才能正确解析,否则就是一堆乱码,或者无法使用。总起来说,对于 Windows 平台,为了保险起见,我们最好用"t"
来打开文本文件,用"b"
来打开二进制文件。对于 Linux 平台,使用"t"
还是"b"
都无所谓,既然默认是"t"
,那我们什么都不写就行了。
六、文件的顺序读写
6.1 顺序读写函数介绍
(1)在顺序写时,先写入的数据存放在文件的前面,后写入的数据存放在文件的后面;
(2)在顺序读时,先读取存放在文件前面的数据,后读取存放在文件后面的数据;
在C语言中,读写文件比较灵活,既可以每次读写一个字符,也可以读写一个字符串,甚至是任意字节的数据(数据块)。可以按照如下表格进行分类:
6.1.1 以字符形式读取文件
以字符形式读写文件时,每次可以从文件中读取一个字符,或者向文件中写入一个字符。主要使用两个函数,分别是 fgetc() 和 fputc()。
1.字符读取函数 fgetc()
fgetc 是 file get char 的缩写,意思是从指定的文件中读取一个字符。fgetc() 的用法为:
int fgetc (FILE *fp);
fp 为文件指针。fgetc() 读取成功时返回读取到的字符,读取到文件末尾或读取失败时返回EOF
。EOF 是 end of file 的缩写,表示文件末尾,是在 stdio.h 中定义的宏,它的值是一个负数,往往是 -1。fgetc() 的返回值类型之所以为 int,就是为了容纳这个负数(char不能是负数)。
用法举例
char ch;
FILE *fp = fopen("D:\\demo.txt", "r+");
ch = fgetc(fp);
表示从
D:\\demo.txt
文件中读取一个字符,并保存到变量 ch 中。
在文件内部有一个位置指针,用来指向当前读写到的位置,也就是读写到第几个字节。在文件打开时,该指针总是指向文件的第一个字节。使用 fgetc() 函数后,该指针会向后移动一个字节,所以可以连续多次使用 fgetc() 读取多个字符。
注意:这个文件内部的位置指针与C语言中的指针不是一回事。位置指针仅仅是一个标志,表示文件读写到的位置,也就是读写到第几个字节,它不表示地址。文件每读写一次,位置指针就会移动一次,它不需要你在程序中定义和赋值,而是由系统自动设置,对用户是隐藏的。
#include<stdio.h>
int main()
{
FILE *fp;
char ch;
//如果文件不存在,给出提示并退出
if( (fp=fopen("D:\\demo.txt","rt")) == NULL )
{
printf("打开文件失败!");
return;
}
//每次读取一个字节,直到读取完毕
while( (ch=fgetc(fp)) != EOF ){
putchar(ch);
}
putchar('\n'); //输出换行符
if(ferror(fp))
{
printf("读取出错");
}
else
{
printf("读取成功");
}
fclose(fp);
fp=NULL;
return 0;
}
在D盘下创建 demo.txt 文件,输入任意内容并保存,运行程序,就会看到刚才输入的内容全部都显示在屏幕上。该程序的功能是从文件中逐个读取字符,在屏幕上显示,直到读取完毕。
程序的关键在于while循环的条件为(ch=fgetc(fp)) != EOF)
。fget() 每次从位置指针所在的位置读取一个字符,并保存到变量 ch,位置指针向后移动一个字节。当文件指针移动到文件末尾时,fget() 就无法读取字符了,于是返回 EOF,表示文件读取结束了。
对EOF的说明:
EOF 本来表示文件末尾,意味着读取结束,但是很多函数在读取出错时也返回 EOF,那么当返回 EOF 时,到底是文件读取完毕了还是读取出错了?我们可以借助 stdio.h 中的两个函数来判断,分别是 feof() 和 ferror()。
feof() 函数用来判断文件内部指针是否指向了文件末尾,它的原型是:
int feof ( FILE * fp ); 当指向文件末尾时返回非零值,否则返回零值。
ferror() 函数用来判断文件操作是否出错,它的原型是:
int ferror ( FILE *fp ); 出错时返回非零值,否则返回零值。
2.字符写入函数fputc()
fputc 是 file output char 的所以,意思是向指定的文件中写入一个字符。fputc() 的用法为:
int fputc ( int ch, FILE *fp );
ch 为要写入的字符,fp 为文件指针。fputc() 写入成功时返回写入的字符,
失败时返回 EOF,返回值类型为 int 也是为了容纳这个负数。
char ch = 'a';
fputc(ch, fp);
表示把字符 'a' 写入fp所指向的文件中。
两点说明
1) 被写入的文件可以用写、读写、追加方式打开,用写或读写方式打开一个已存在的文件时将清除原有的文件内容,并将写入的字符放在文件开头。如需保留原有文件内容,并把写入的字符放在文件末尾,就必须以追加方式打开文件。不管以何种方式打开,被写入的文件若不存在时则创建该文件。
2) 每写入一个字符,文件内部位置指针向后移动一个字节。
从键盘输入一行字符,写入文件
#include<stdio.h>
int main()
{
FILE *fp;
char ch;
//判断文件是否成功打开
if( (fp=fopen("D:\\demo.txt","wt+")) == NULL )
{
printf("打开文件失败!");
exit(0);
}
printf("请输入一个字符串:\n");
//每次从键盘读取一个字符并写入文件
while ( (ch=getchar()) != '\n' )
{
fputc(ch,fp);
}
fclose(fp);
fp=NULL;
return 0;
}
运行程序,输入一行字符并按回车键结束,打开D盘下的 demo.txt 文件,就可以看到刚才输入的内容。程序每次从键盘读取一个字符并写入文件,直到按下回车键,while 条件不成立,结束读取。
6.1.2 以字符串形式读取文件
1. 字符串读取函数fgets()
fgetc() 和 fputc() 函数每次只能读写一个字符,速度较慢;实际开发中往往是每次读写一个字符串或者一个数据块,这样能明显提高效率。fgets() 函数用来从指定的文件中读取一个字符串,并保存到字符数组中,它的用法为:
char *fgets ( char *str, int n, FILE *fp );
str 为字符数组,n 为要读取的字符数目,fp 为文件指针。
返回值:读取成功时返回字符数组首地址,也即 str;读取失败时返回 NULL;如果开始读取时文件内部指针已经指向了文件末尾,那么将读取不到任何字符,也返回 NULL。
注意,读取到的字符串会在末尾自动添加 '\0',n 个字符也包括 '\0'。也就是说,实际只读取到了 n-1 个字符,如果希望读取 100 个字符,n 的值应该为 101。需要重点说明的是,在读取到 n-1 个字符之前如果出现了换行,或者读到了文件末尾,则读取结束。这就意味着,不管 n 的值多大,fgets() 最多只能读取一行数据,不能跨行。在C语言中,没有按行读取文件的函数,我们可以借助 fgets(),将 n 的值设置地足够大,每次就可以读取到一行数据。
#define N 101
char str[N];
FILE *fp = fopen("D:\\demo.txt", "r");
fgets(str, N, fp);
表示从 D:\\demo.txt 中读取 100 个字符,并保存到字符数组 str 中。
一行一行地读取文件
#include <stdio.h>
#include <stdlib.h>
#define N 100
int main()
{
FILE *fp;
char str[N+1];
if( (fp=fopen("d:\\demo.txt","rt")) == NULL )
{
printf("打开文件失败!");
exit(0);
}
打印获取的字符串
while(fgets(str, N, fp) != NULL)
{
printf("%s", str);
}
fclose(fp);
fp=NULL;
return 0;
}
fgets() 遇到换行时,会将换行符一并读取到当前字符串。该换行的地方换行,就是因为 fgets() 能够读取到换行符。而 gets() 不一样,它会忽略换行符。
2. 字符串写入函数fputs()
fputs() 函数用来向指定的文件写入一个字符串,它的用法为:
int fputs( char *str, FILE *fp );
str 为要写入的字符串,fp 为文件指针。写入成功返回非负数,失败返回 EOF。
如下:
char *str = "biancheng";
FILE *fp = fopen("D:\\demo.txt", "at+");
fputs(str, fp);
表示把把字符串 str 写入到 D:\\demo.txt 文件中
6.1.3 格式化读写文件
fscanf() 和 fprintf() 函数与前面使用的 scanf() 和 printf() 功能相似,都是格式化读写函数,两者的区别在于 fscanf() 和 fprintf() 的读写对象不是键盘和显示器,而是磁盘文件。
这两个函数的原型为:fprintf() 返回成功写入的字符的个数,失败则返回负数。fscanf() 返回参数列表中被成功赋值的参数个数。
如果将 fp 设置为 stdin,那么 fscanf() 函数将会从键盘读取数据,与 scanf 的作用相同;设置为 stdout,那么 fprintf() 函数将会向显示器输出内容,与 printf 的作用相同。
int fscanf ( FILE *fp, char * format, ... ); int fprintf ( FILE *fp, char * format, ... );
#include<stdio.h>
int main()
{
int a, b, sum;
fprintf(stdout, "Input two numbers: ");
fscanf(stdin, "%d %d", &a, &b);
sum = a + b;
fprintf(stdout, "sum=%d\n", sum);
return 0;
}
6.1.4 以数据块形式读写文件
1. fread()函数
fread() 函数用来从指定文件中读取块数据。所谓块数据,也就是若干个字节的数据,可以是一个字符,可以是一个字符串,可以是多行数据,并没有什么限制。
参数说明:
- buffer:是一个地址,用来存放读取到的数据存储到的地址,即读到哪里去;
- size:要读取每个数据项的字节数;
- count:要读取多少个数据项;
- stream:要读取的数据源,文件指针(从哪读)
返回值:
返回成功读写的块数,也即 count。如果返回值小于 count,
- 对于 fread() 来说,可能读到了文件末尾,可能发生了错误,可以用 ferror() 或 feof() 检测。
2.fwrite()函数
fwrite() 函数用来向文件中写入块数据。
参数说明:
- buffer:是一个地址,要把此地址开始的存储区中的数据向文件写入,即从哪写
- size:要写入的每个数据项的字节数;
- count:要写入多少个数据项;
- stream:要写入的数据源,文件指针(写到哪里去)
返回值:
返回成功读写的块数,也即 count。如果返回值小于 count,
- 对于 fwrite() 来说,肯定发生了写入错误,可以用 ferror() 函数检测。
6.2 对比一组函数
第一组:scanf、fscanf、sscanf
第二组:printf、fprintf、sprintf
#include<stdio.h>
struct S
{
char arr[10];
int age;
float score;
};
int main()
{
struct S s = { "zhangsan", 20, 55.5f };
struct S tmp = { 0 };
char buf[100] = { 0 };
//把s中的格式化数据转化成字符串放到buf中
sprintf(buf, "%s %d %f", s.arr, s.age, s.score);
printf("字符串:%s\n", buf);
//从字符串buf中获取一个格式化的数据到tmp中
sscanf(buf, "%s %d %f", tmp.arr, &(tmp.age), &(tmp.score));
printf("格式化:%s %d %f\n", tmp.arr, tmp.age, tmp.score);
return 0;
}
七、文件的随机读写
前面介绍的文件读写函数都是顺序读写,即读写文件只能从头开始,依次读写各个数据。但在实际开发中经常需要读写文件的中间部分,要解决这个问题,就得先移动文件内部的位置指针,再进行读写。这种读写方式称为随机读写,也就是说从文件的任意位置开始读写。一首歌从头开始听就是顺序读,如果你直接拖动进度条到第 1 分钟时开始听就 是随机读。看电影时直接跳过片头也是随机读。 要实现随机读写操作,最重要的东西就是文件位置标记(也称文件位置指针), 这个标记类似光标,标记接下来要读写的开始位置。
实现随机读写的关键是要按要求移动位置指针,这称为文件的定位。
7.1 fseek函数
根据文件指针的位置和偏移量来定位文件指针。函数原型如下:
参数说明:
1) stream为文件指针,也就是被移动的文件。
2) offset 为偏移量,也就是要移动的字节数。之所以为 long 类型,是希望移动的范围更大,能处理的文件更大。offset 为正时,向后移动;offset 为负时,向前移动。
3) origin 为起始位置,也就是从何处开始计算偏移量。C语言规定的起始位置有三种,分别为文件开头、当前位置和文件末尾,每个位置都用对应的常量来表示:
7.2 ftell函数
测定文件位置标记当前的位置; 返回值是当前位置距离文件开头的字节数。函数原型如下:
7.3 rewind函数
rewind() 用来将位置指针移动到文件开头。函数原型如下:
void rewind ( FILE * stream );
八、文件读取结束的判定
8.1 被错误使用的 feof函数
牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。
以上便是文件操作全部内容,认真理解消化,一定会有极大的收获,可以留下你们点赞、关注、评论,您的支持是对我极大的鼓励,下期再见!