一、文件的作用及简单分类
文件的作用:使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。
在程序设计中,我们一般将文件分为两种:程序文件、数据文件(从文件功能的角度来分类的)。
程序文件:包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)等。
数据文件:文件的内容不是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
文件名:一个文件要有一个唯一的文件标识,以便用户识别和引用,这个标识叫做文件名。
文件名包含3部分:文件路径+文件名主干+文件后缀
例如: c:\code\test.txt
二、执行文件操作
下面讲解的是对数据文件的操作。
数据文件:数据文件可分为:文本文件和二进制文件两类。
二进制文件:数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
文本文件:如果要求数据在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件(即使是数字,也是转换成字符数字,用其对应的ASCII值表示)。
文件缓冲区:ANSIC 标准采用“缓冲文件系统”来处理的数据文件,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的输出缓冲区,而从磁盘向计算机读入数据,则先将磁盘文件中读取的数据输入到内存中的输入缓冲区。缓冲区的大小根据C编译系统决定的。
文件缓冲机制默认是等输入输出缓冲区充满后才将缓冲区内的数据传到程序数据区或硬盘,当然也可以通过主动刷新来释放缓冲区。
fflush函数可以刷新缓冲区,fclose等函数执行时也会自动刷新缓冲区。
- 文件的打开和关闭
文件指针:每个被使用的文件都会在内存中开辟一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息保存在一个结构体变量中,这个结构体的类型系统声明的,取名FILE。
程序员一般都是通过一个FILE的指针来维护这个FILE结构的变量,这个FILE指针就是文件指针。
创建一个文件指针变量:
FILE* pf; //文件指针变量
可使pf指向某个文件的文件信息区,通过该文件信息区中的信息就能够访问该文件,所以通过文件指针变量能够找到与信息对应的文件,进而对文件进行操作。
操作文件前必须先打开文件,文件使用结束之后应该关闭文件。
fopen函数:打开文件。
FILE* fopen ( const char* filename, const char* mode );
参数:第一个参数传 要打开文件的文件名,第二个参数传 文件的打开方式。
返回值:返回一个FILE*的指针变量指向该文件,所以要用文件指针变量接收;文件打开失败返回NULL。
//示例:
FILE* pf = fopen("myfile.txt", "r"); //以“只读”的形式打开文件
if (pf == NULL) //检验是否打开成功
{
perror("fopen");
}
文件打开后要检验是否打开成功。
文件的打开方式:
文件打开方式 | 含义 | 如果指定文件不存在 |
---|---|---|
“r”(只读) | 为了读取数据,打开一个已存在的文本文件 | 报错 |
“w”(只写) | 为了写入数据,打开一个文本文件 | 新建出这个文件 |
“a”(追加) | 向一个文本文件末尾添加数据 | 新建出这个文件 |
“rb”(只读) | 为了读取数据,打开一个已存在的二进制文件 | 报错 |
“wb”(只写) | 为了写入数据,打开一个二进制文件 | 新建出这个文件 |
“ab”(追加) | 向一个二进制文件末尾添加数据 | 报错 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 报错 |
“w+”(读写) | 为了读和写,建立一个新的文本文件 | 新建出这个文件 |
“a+”(读写) | 打开一个文件,在文件末尾进行读写 | 新建出这个文件 |
“rb+”(读写) | 为了读和写,打开一个二进制文件 | 报错 |
“wb+”(读写) | 为了读和写,新建一个新的二进制文件 | 新建出这个文件 |
“ab+”(读写) | 打开一个二进制文件,在文件末尾进行读写 | 新建出这个文件 |
【注】:每次以"w"方式打开文件,这个文件原来的内容都会被覆盖。
fclose函数:关闭已打开的文件。
int fclose ( FILE* stream );
参数:传已打开的那个文件的文件指针变量。
返回值:关闭成功,返回0;关闭失败,返回EOF。fclose函数不需要接收返回值,直接写就行了。
//示例:
fclose(pf); //关闭pf指向的那个文件
- 文件的顺序读写
函数 | 功能 | 适用范围 |
---|---|---|
fputc | 将一个字符写入输出流 | 所有输出流 |
fgetc | 从输入流读取一个字符 | 所有输入流 |
fputs | 向输出流写入一行字符 | 所有输出流 |
fgets | 从输入流读取一行字符 | 所有输入流 |
fprintf | 将数据格式化写入流中 | 所有输出流 |
fscanf | 从流中格式化读取数据 | 所有输入流 |
fwrite | 将数据以二进制形式写入文件 | 文件 |
fread | 读取二进制文件 | 文件 |
(输入输出都是相对内存的,数据从内存流出就是输出,而流入内存就是输入。C语言库函数查询——上面的函数的详解都可以在这里查询)
【注】:1、fgetc函数每次读取完成后,对应该文件的字符定位指针就会向后挪一位,所以下一次读取同一文件会读取到下一个字符。
2、fgets函数读取的字符串的最后一个字符会被替换为 ‘\0’,所以参数传的是读取n个,实际有效读取的只有 (n-1)个。
3、用输入函数读取的内容都要用变量存储,即输入函数的返回值都需要接收。
//fpirntf、fscanf、fread和fwrite的示例
fprintf(pf,"%s %d","haha",666); //将"haha"和666以字符串和整型的格式写入pf指向的文件中
fscanf(pf,"%s %d",str,&num); //将pf指向的文件中的字符串输入str(字符数组名)中,数字输入num(一个整型变量)中
fread(&S,sizeof(struct S),1,pf); //将一个结构体变量中的内容以二进制形式写入文件中
fwrite(&S,sizeof(struct S),1,pf); //将二进制文件的内容写入一个结构体变量中
- 文件的定位读写
fseek函数:指定位置读取一个字符。
int fseek( FILE* stream, long int offset, int origin );
参数:第一个参数传文件指针,第二个参数传相对参考位置的偏移量(向后偏移为正数,向前偏移为负数),第三个参数传参考位置。
返回值:返回读取到字符的ASCII值。
参考位置(三选一):
参考位置常数 | 含义 |
---|---|
SEEK_SET | 文件初始位置 |
SEEK_CUR | 文件指针当前位置 |
SEEK_END | 文件末尾位置 |
【注】:偏移量的计算是指针指向的那个字符偏移量是0,往后挪一位偏移量+1,往前挪一位偏移量加 -1。
ftell函数:计算文件指针相对于起始位置的偏移量。
long int ftell( FILE* stream );
- 文件读取结束的判定
feof函数:判断文件是否正常结束。
int feof( FILE* stream );
ferror函数:判断文件是否因出现错误而结束。
int ferror( FILE* stream );
【注】:feof函数和ferror函数是在文件读取结束后判断文件结束原因的函数,不是用来判断文件什么时候结束的函数。
feof —— 返回真,说明文件是正常读取到结束标志而结束的。
ferror —— 返回真,说明文件是在读取过程中因出错而结束的。
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");