Loading... # 文件 ## 1、文件基本概念 C程序把文件分为**ASCII文件**和**二进制文件**,ASCII文件又称**文本文件**,二进制文件和文本文件(也称ASCII码文件)二进制文件中,数值型数据是以二进制形式存储的, 而在文本文件中,则是将数值型数据的每一位数字作为一个字符以其ASCII码的形式存储,因此,文本文件中的每一位数字都单独占用一个字节的存储空间, 而二进制文件则是把整个数字作为一个二进制数存储的,**并非数值的每一位数字都占用单独的存储空间**,无论一个C语言文件的内容是什么, 它一律把数据看成是字节构成的序列,即字节流,对文件的存取也是以字节为单位的,输入/输出的的数据流仅受程序控制而不受物理符号(如回车换行符)的控制,所以说C语言文件为**流式文件** C语言的文件存取有两种方式:**顺序存取**和**随机存取** **需要理解的知识点包括**: 1. 数据流 2. 缓冲区 3. 文件类型 4. 文件存取方式 ### 数据流: 指程序与数据的交互是以流的形式进行的。进行C语言文件的存取时,都会先进行“打开文件”操作,这个操作就是在打开数据流,而“关闭文件”操作就是关闭数据流。 ### 缓冲区(Buffer): 指在程序执行时,所提供的额外内存,可用来暂时存放做准备执行的数据。它的设置是为了提高存取效率,因为内存的存取速度比磁盘驱动器快得多。 C语言中带缓冲区的文件处理: C语言的文件处理功能依据系统是否设置“缓冲区”分为两种:一种是设置缓冲区,另一种是不设置缓冲区。由于不设置缓冲区的文件处理方式,必须使用较低级的I/O函数(包含在头文件`io.h`和`fcntl.h`中来直接对磁盘存取,这种方式的存取速度慢,并且由于不是C的标准函数,跨平台操作时容易出问题。下面只介绍第一种处理方式,即设置缓冲区的文件处理方式: 当使用标准I/O函数(包含在头文件`stdio.h`中时,系统会自动设置缓冲区,并通过数据流来读写文件。当进行文件读取时,不会直接对磁盘进行读取,而是先打开数据流,将磁盘上的文件信息拷贝到缓冲区内,然后程序再从缓冲区中读取所需数据,如下图所示: ![输入图片说明](/images/Cimages/file-master/01.jpg) 事实上,当写入文件时,并不会马上写入磁盘中,而是先写入缓冲区,只有在缓冲区已满或“关闭文件”时,才会将数据写入磁盘,如下图所示。 ![输入图片说明](/images/Cimages/file-master/02.jpg) ### 文件类型: 分为**文本文件**和**二进制文件**两种。 文本文件是以**字符编码**的方式进行保存的。二进制文件将内存中数据原封不至文件中,适用于非字符为主的数据。如果以记事本打开,只会看到一堆乱码。 其实,除了文本文件外,所有的数据都可以算是二进制文件。二进制文件的优点在于存取速度快,占用空间小,以及可随机存取数据。 ### 文件存取方式: 包括顺序存取方式和随机存取方式两种。 顺序读取也就是从上往下,一笔一笔读取文件的内容。保存数据时,将数据附加在文件的末尾。这种存取方式常用于文本文件,而被存取的文件则称为顺序文件。 随机存取方式多半以二进制文件为主。它会以一个完整的单位来进行数据的读取和写入,通常以结构为单位。 ## 文本文件操作 C语言中主要通过标准I/O函数来对文本文件进行处理。相关的操作包括打开、读写、关闭与设置缓冲区。 相关的存取函数有:`fopen()`, `fclose()`, `fgetc()`, `fputc()`, `fgets()`, `fputs()`,`fprintf()`, `fscanf()`等。 注意:应提前建立一个.text文本文件,放到与.c文件同一个文件夹中 `fopen()` 打开文件函数 (成功打开后指向该流的文件指针就会被返回,失败返回NULL) `fclose()` 关闭文件函数 **文件的使用方式和含义**: <table cellspacing="1" cellpadding="1" border="0" align="center"><tbody><tr><td style="text-align:center;">打开方式</td><td style="text-align:center;">含义</td><td style="text-align:center;">指定文件不存在时</td><td style="text-align:center;">指定文件存在时</td></tr><tr><td>r</td><td>只读方式打开文本文件</td><td>出错</td><td>正常打开</td></tr><tr><td>w</td><td>只写方式打开文本文件</td><td>建立新文件</td><td>文件原有内容丢失</td></tr><tr><td>a</td><td>追加方式打开文本文件</td><td>建立新文件</td><td>在原有内容末尾追加</td></tr><tr><td> <p>r+</p> </td><td>读/写方式打开文本文件</td><td>出错</td><td>正常打开</td></tr><tr><td>w+</td><td>读/写方式创建新的文本文件</td><td>建立新文件</td><td>文件原有内容丢失</td></tr><tr><td>a+</td><td>读/追加方式建立新的文本文件</td><td>建立新文件</td><td>在原有内容末尾追加</td></tr><tr><td>rb</td><td>只读方式打开二进制文件</td><td>出错</td><td>正常打开</td></tr><tr><td>wb</td><td>只写方式打开二进制文件</td><td>建立新文件</td><td>文件原有内容丢失</td></tr><tr><td>ab</td><td>追加方式打开二进制文件</td><td>建立新文件</td><td>在原有内容末尾添加</td></tr><tr><td>rb+</td><td> <p>读/写方式打开二进制文件</p> </td><td>出错</td><td>正常打开</td></tr><tr><td>wb+</td><td>读/写方式创建新的二进制文件</td><td>建立新文件</td><td>文件原有内容丢失</td></tr><tr><td>ab+</td><td>读/追加方式创建新的二进制文件</td><td>建立新文件</td><td>在原有内容末尾追加</td></tr></tbody></table> ### 一、字符读写 #### 1、字符写入文件函数 `fputc` **`fputc`函数的原型如下**: ```c int fputc( int c, FILE *fp ); ``` **参数说明**: 其中,c是要写入的字节,它虽被定义为整型,但只使用最低位的一字节,fp是文件指针。 **`fputc`的功能**: 将字节c输出至fp所指向的文件。如果成功,位置指针自动后 移1字节的位置,并且返回c;否则返回EOF。 #### 2、从文件中读取字符 `fgetc` **`fgetc`函数的原型如下**: ```c int fgetc( FILE *fp ); ``` **参数说明**: 其中`fp`为文件指针。 **`fgetc`的功能**: 从`fp`所指向的文件中读取一个字节,如果成功则返回读取的字节,位置指针自动后移1字节的位置;否则返回EOF。 ### 二、字符串的读写 #### 1、字符串写入文件函数 `fputs` **`fputs`函数的原型如下**: ```c int fputs( const char *s, FILE *fp ); ``` **参数说明**: 其中,s是要写入的字符串,fp是文件指针。 **`fputs`的功能**: 将字符串s输出至`fp`所指向的文件(不含'\0')。如果成功,位置指针自动后移,函数返回一个非负整数;否则返回EOF。 #### 2、从文件中读取字符 `fgets` **`fgets`函数的原型如下**: ```c char *fgets( char *s, int n, FILE *fp ); ``` **参数说明**: 其中,s指向待赋值字符串的首地址,n是控制读取个数的参数,fp为文件指针。 **`fgets`的功能**: 从位置指针开始读取 一行或n-1个字符,并存入s,存储时自动在字符串结尾加上'\0'。如果函数执行成功,位置指针自动后移, 并返回s的值,否则返回NULL。 ### 三、块数据读写 所谓块读写,就是读写n块以m个字节为单位的二进制数据,可以是一个字符**(一个字符为一字节,则块大小为1*1)**,可以是一个长度为n字符串**(块大小1*n)**,可以是长度为n的整型数组**(整型以4字节算,块大小4n)**,也可以是结构体等任意数据类型,并没有什么限制。 #### 1、向文件中写入块数据`fwrite` **`fwrite`函数的原型如下:** ```c size_t fwrite ( void * ptr, size_t size, size_t count, FILE *fp ); ``` **参数说明:** `ptr`:指向保存读写数据的内存的指针,它可以指向数组、变量、结构体等。 `size`:表示每个数据块的字节数。 `count`:表示要读写的数据块的块数。 `fp`:表示文件指针。 理论上,每次读写 size*count 个字节的数据。 **`fwrite`的功能:** 从内存中的ptr指向的地址开始,将连续n*size字节的内容写入fp文件中。该函数的返回值是实际写入的数据块个数。 #### 2、从文件中读取块数据`fread` **`fread`函数的原型如下**: ```c size_t fread ( void *ptr, size_t size, size_t count, FILE *fp ); //size_t 是在 stddef.h 头文件中使用 typedef 定义的数据类型,表示无符号整数,也即非负数,常用来表示数量。 ``` **参数说明**: 同`fwrite` **`fread`的功能**: 从文件fp中,连续读取n*size字节的内容,并存入ptr指向的内存空间。该函数的返回值是实际读入的数据块个数。 ### 四、格式化读写 格式化读写函数包括`fprintf`和`fscanf`两个函数,它们只用于文本文件的读写,不能用于二进制文件的读写。文本文件与二进制文件的区别下面注意点中有介绍。 #### 1、格式化写入文件`fprintf` **`fprintf`函数的原型如下**: ```c int fprintf( FILE *fp, const char* format, 输出参数1, 输出参数2… ); ``` **参数说明**: 其中,fp是文件指针,format为格式控制字符串,输出参数列表为待输出的数据。 **`fprintf`的功能**: 根据指定的格式(format参数)发送数据(输出参数)到文件fp。 > 例: ```c #include <stdio.h> int main() { FILE *fp; fp = fopen("a.txt","w"); int a = 10; double f = 11.11; fprintf(fp, "%d%lf", a, f); fclose(fp); return 0; } 注意:fprintf()按格式输入到流,其用法和printf()相同,不过不是写到控制台,而是写到流罢了。注意的是 返回值为此次操作写入到文件的字节数。如int c =fprintf(fp, "%s %s %d %f", str1,str2, a, b) ; 假设str1:10字节;str2:10字节;a:2字节;b:8字节;则最终c为33,因为写入时不同的数据间自动加入一个空格。 ``` #### 2、从文件中格式化读取`fscanf` **`fscanf`函数的原型如下:** ```c int fscanf( FILE *fp, const char* format, 地址1,地址2… ); ``` **函数说明**: 其中,fp是文件指针,format为格式控制字符串,地址列表为输入数据的存放地址。 **`fscanf`的功能**: 根据指定的格式(format参数)从文件fp中读取数据至内存(地址)。 > 例: ```c #include <stdio.h> int main() { FILE *fp; fp = fopen("a.txt","r"); //需要创建a.txt文件,然后写入两个数据,空格隔开 int i=0; double f=0; fscanf( fp, "%d%lf", &i, &f ); fclose(fp); printf("%d\n%lf\n",i,f); return 0; } ``` ### 五、总结 #### 1、`fputc`和`fgetc`注意点 `fputc` 每次只能存一个字节,如果将整型、浮点型等数据类型写入文件(因为整型浮点型每个数据都需要好几个字节,由于平台不同不固定,具体自己查不列出),就会造成数据只截取最后一个字节导致数据出错;同理`fgetc`每次也只能读取一个字节。这时用 `fprintf` 和 `fscanf` 函数格式化读写比较合适。 `fgetc` 每次调用只能获取一个字节,如果是中文字符,utf8 编码会占用三个字节,因此要用 `fgets` 来读取。当然你不嫌累也可以用 `fgetc` 读三次,然后拼成一个中文字符。 #### 2、`fputs`和`fgets`注意点 `fgets` 有局限性,每次最多只能从文件中读取一行内容,因为 `fgets` 遇到换行符就结束读取。如果希望读取多行内容,需要使用 `fread` 函数;相应地写入函数为 `fwrite`。 #### 3、`fwrite`和`fread`注意点 `fwrite`和`fread`直接读写二进制文本。 ## 常用文件函数及功能 | 分类 | 函数名 | 功能 | | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 打开文件 | fopen() | 打开文件。 | | 关闭文件 | fclose() | 关闭文件。 | | 文件定位 | fseek()<br />rewind() <br />ftell() | 改变文件位置指针位置<br />使文件位置指针重新置于文件开头返回文件位置<br />指针的当前值 | | 文件读写 | fgetc(),getc()<br />fputc(),putc()<br />fgets()<br />fputs()<br />getw()<br />putw()<br />fread()<br />fwrite()<br />fscanf()<br />fprintf() | 从指定文件取得一个字符。<br />把字符输出到指定文件。<br />从指定文件读取字符串。<br /> 把字符串输出到指定文件。<br />从指定文件读取一个字(int型)。<br /> 把一个字(int型)输出到指定文件。<br /> 从指定文件中读取数据项。<br /> ``` 把数据项写到指定文件。 ``` <br />从指定文件按格式输入数据。<br /> 按指定格式将数据写到指定文件中。 | | 文件状态 | feof()<br />ferror()<br />clearerr() | 若到文件末尾,函数值为“真”(非0)<br />。 若对文件操作出错,函数值为“真”(非0)。<br /> ``` 使ferror和feof函数值置零。 ``` | 最后修改:2022 年 05 月 30 日 © 允许规范转载 打赏 赞赏作者 赞 如果觉得我的文章对你有用,请随意赞赏