C语言文件处理
文件与流
将数据存储在变量或数组中,数据在程序运行结束后这些数据将会被释放,而文件则可以永久地保存数据。
C语言中,文件不过是一个按顺序组成地字节流,流就是字节的序列。
当一个文件被打开时,就会有一个流与这个文件联系在一起。当一个程序开始执行时,会有三个流被自动打开:
- 标准输入(stdin)
- 标准输出(stdout)
- 标准错误 (stderr)
流提供了文件与程序之间进行信息交换的通道。
每次打开一个文件都会返回一个指向FILE结构体类型(在头文件stdio.h中定义)的指针,FILE结构体类型包含了用于文件处理的信息。
在多数操作系统中,这个结构体中包含一个文件描述头,即针对操作系统中打开文件列表的一个数组的整数索引,数组中的每个元素包含有一个文件控制块(FCB),而操作系统正是利用文件控制块(FCB)中的信息来管理一个特定的文件。
每个文件,要么以一个文件结束标记标记文件的结束,要么以记录在系统数据结构中的指定数目的字节数作为文件的结束,这个由程序所运行平台决定。
文件处理函数
以下面这个程序为例展开说明。
这个程序用于记录客户的账号 姓名 和 余额信息,将输入的客户信息保存到文件中。
#include<stdio.h>
int main(){
FILE *cfPtr = fopen("clients.txt","w");
if(cfPtr == NULL){
puts("文件无法打开!");
}
else{
puts("输入 账号 、 姓名 和 余额,按下 ctrl+z 回车 结束输入:");
printf("? ");
unsigned int account;
char name[30];
double balance;
scanf("%d%29s%lf", &account, name, &balance);
while(!feof(stdin)){
fprintf(cfPtr,"%d %s %.2f\n", account, name, balance);
printf("? ");
scanf("%d%29s%lf", &account, name, &balance);
}
}
fclose(cfPtr);
}
FILE*文件指针
程序开始定义了一个FILE*
类型的变量cfPtr,cfPtr是一个指向FILE结构体类型的指针。C程序总是用一个单独的FILE结构体来管理每个文件,每个打开的文件都有一个单独声明的、类型FILE的指针,该指针用来实现对文件的访问,通过FILE结构体来间接地访问操作系统中相应的文件的文件控制块(FCB)。
函数fgetc和fputc
函数fgetc需要一个文件指针作为实参,作用是向传入的文件指针指向的目标文件读入一个字符,如果传入的参数是stdin,fgetc(stdin)作用等同于函数getchar();
函数fputc需要一个字符型(char)和一个文件指针作为实参,作用是向传入的文件指针指向的目标文件写入传入的实参字符,例如,fputc(‘a’,stdout)的作用等同于putchar(‘a’)。
函数fopen
函数fopen的返回值是指向其所打开文件的FILE结构体类型的指针,若文件未成功打开,fopen返回NULL。
fopen有两个实参:
- 文件名(可以包括表示文件存储位置的路径信息)
- 文件的打开模式
FILE *cfPtr = fopen("clients.txt","w");
这条语句的含义是:以文件打开模式"w",建立起一个与名为client的文件进行通信的通道。
函数fopen的返回值是指向其所打开文件的FILE结构体类型的指针,被赋值给了文件指针变量cfPtr,若文件未成功打开,fopen返回NULL。
文件打开模式说明
模式 | 说明 |
---|---|
r | 为读操作打开一个已存在的文件 |
w | 为写操作创建一个文件,若文件存在,则丢弃其中的当前内容 |
a | 为在文件末尾进行写而打开或创建一个文件,即给文件添加数据的写操作 |
r+ | 为更新(读/写)而打开一个已存在的文件 |
w+ | 为更新而创建一个文件,若文件已存在,则丢弃其中的内容 |
a+ | 为读和更新打开或创建一个文件,所有的写都在文件末尾进行 |
rb | 以二进制读模式,为读操作打开一个已存在的文件 |
wb | 以二进制模式,为写操作打开一个已存在的文件,若文件存在,丢弃其中内容 |
ab | 添加:以二进制模式,为在文件末尾进行的写操作打开或者创建一个文件 |
rb+ | 以二进制模式,为更新(读/写)二打开一个已存在的文件 |
wb+ | 以二进制模式,为更新而创建一个文件,若文件存在,丢弃其中内容 |
ab+ | 添加:以二进制模式,为更新而打开或者创建一个文件;写操作在文件末尾进行 |
此外,C11还通过在模式w、w+、wb或wb+后面增加一个x,提供互斥写模式。在互斥写模式下,如果文件已经存在或者不能被创建,则函数fopen的执行将失败。
若能按照互斥写模式成功打开一个文件,且底层系统支持互斥文件访问,则在该文件被打开的时间段内,只有你的程序能够访问它。
函数feof
函数feof的实参是一个指向欲测试是否带有文件结束标记的文件指针,若文件结束标记已经被设置,则函数返回一个非零值,否则返回0。
while(!feof(stdin))
这条语句中,函数feof检查是否为标准输入stdin检测到文件结束标记。文件结束标记用于通知程序不再有数据需要处理了。
本条语句中,当用户键入代表文件结束的组合键时,文件结束标记将被写入stdin中。
本条语句中,在stdin中文件结束标记未被设置前,while循环会一直执行。
函数fprintf
函数fprintf的作用是向文件写入数据。
函数fprintf基本上是等价于函数printf的,只不过fprintf函数需要多接收一个文件指针实参,作为数据写入的目标文件。
如果将stdout作为文件指针,则fprintf函数将数据输出到标准输出中。
示例:fprintf(stdout," %d %s %.2f\n",66,"hello",66.66);
函数fscanf
函数fcanf的作用也基本是等价于函数scanf的,函数fcanf需要多接收一个人家指针作为实参,这个文件指针指向的文件就是要读取数据的目标文件。fscanf(cfPtr,"%d%29s%lf",&accont,name,&balance);
上面这条语句,程序会将cfPtr所指向的目标文件读出数据,按顺序把数据放入对应的地址。
函数fscanf的返回值是成功读取的数据项个数,若读取数据过程发生了问题,则返回EOF。
函数fwrite和fread
函数fwrite的功能是:将从某个特定地址开始存储的特定数目的字节数据,从内存中转移到一个文件,其中参数“文件位置指针”指示的是文件中这些字节数据被写入的起始地址。
函数fread的功能是:将文件位置指针指示的特定位置开始的特定数目的字节数据,从一个文件中转移到某个特定地址开始的一个内存区域中。
例如:
fwrite(&number, sizeof(int), 1, fPtr);
上面这条语句的意思是从number的首地址开始,向文件指针fPtr指向的目标文件写入1个大小为sizeof(int)个字节的数据项。返回值为成功输出的数据项的个数。
fread(&number, sizeof(int), 1, fPtr);
fread和fwrite相反,该语句是:从fPtr指向的目标文件的文件指针位置开始读出1个大小为sizeof(int)个字节的数据项,从number首地址开始存。返回值为所读数据项的个数。若返回值小于第三个实参,说明出现了读错误。
函数rewind
该函数用于文件位置指针(文件偏移量)的复位。
为了从顺序文件中提取特定的数据,程序通常是从文件头开始连续的读取数据,直到发现欲提取的数据为止。程序在执行过程中,可能需要对顺序存取文件中特定的数据进行多次处理(每一次都从头开始)。这时可以调用下面这个函数:
rewind(cfPtr);
将程序的文件位置指针,指示文件中下一个将被读/写的字节编号,重新定位在cfPtr所指向文件的开头(即字节编号为0)。
文件位置指针实际上并不是一个指针,而是一个整数值,这个整数值表示文件中下一个读/写操作将发生在哪个字节位置上,也称为文件偏移量。
文件偏移量时与文件相联系的FILE结构体类型的一个成员。
函数fseek
函数fseek的原型为:
int fseek(FILE *stream, long int offset, int whence);
其中,offset是stream所指向文件的,从whence位置开始,文件位置指针(文件偏移量)将要搜索扫过的字节数——正的offset表示向前扫,负表示向后扫。
表示文件位置的搜索起始位置的实参whence有三个可选值:
SEEK_SET ,SEEK_CUP,SEEK_END
。
SEEK_SET表示搜索的起始位置为文件的开头位置,SEEK_CUR表示搜索的起始位置为文件的当前位置,SEEK_END表示偏移量从文件尾部开始计算。
函数fseek返回一个非0值,即定位到文件开始位置之前。
函数fclose
当用户键入文件结束标记后,程序将用函数fclose关闭文件。
函数fclose的参数是一个文件指针,而不是文件名。
若程序中没有显示地调用fclose,那么程序会在结束退出时,关闭所有未关闭地文件。主要是程序结束退出时,而不是函数结束退出。
关闭一个文件能够释放掉其所占用的资源。其他用户或程序也许在等待使用这些资源。一旦明确程序不在访问一个文件后,应立即关闭这个文件,而不是等程序结束退出操作系统来关闭。
示例:fclose(cfPtr);