使用文件的原因
我们写的程序的数据是暂时储存在电脑的内存中的,如果程序停止,内存就会被回收,数据也就丢失了。而使用文件可以对数据进行持久化的保存。
概念
文件包括程序文件、数据文件,程序文件可以分为源程序文件(.c),目标文件(.obj),可执行程序(.exe)等等。每个文件有对应的文件名,结构为文件路径+文件名主干+文件后缀,例如C:\code\test.txt
文件还可以被分为二进制文件和文本文件,文本文件的内容就是我们平时所看到的文件内容,而二进制文件的内容为人工无法识别的代码。文件概念的有关细节不在这里作过多介绍。
打开和关闭文件
一、流和标准流
C程序针对文件、画面、键盘等的数据输入或输出都是通过流来完成的(这里的流是抽象出来的概念)。一般向流里写数据,或者从流中读取数据,都是打开流进行操作。
我们平时一般感受不到打开流这一操作,主要是C语言程序在启动时默认打开了3个流:
stdin——标准输入流、stdout——标准输出流、stderr——标准错误流 这些流分别与我们的键盘输入和显示器输出有关。他们的类型是FILE*,需要介绍到下面的概念。
二、文件指针
每个文件在使用时在内存中开辟了一个相应的文件信息区,信息保存在一个结构体变量中,该结构体类型取名FILE。如下定义了文件指针pf:
FILE* pf;
通过文件指针可以间接操作相应的文件。
三、打开与关闭
文件操作的基本流程是:读写之前,打开文件,使用后,关闭文件。
fopen函数用来打开文件,相应的介绍:fopen - C++ Reference。格式为:fopen(文件名,打开方式) 打开后会返回一个FILE*的指针变量。
fclose函数用来关闭文件,相应的介绍:fclose - C++ Reference。格式为:fclose(文件名);
上面提到的打开方式主要可以分为以下几种:
①“r”:只读,用以向文本文件输入数据,如果打开失败,则返回NULL。“w”:只写,用以向文本文件输出数据,打开失败(没有对应的文件),则按文件名建立一个文件再操作。“a”:追加,向文本文件末尾添加数据,文件不存在则建立一个新的文件。
与之对应的还有 “rb” “wb” “ab”三种打开方式,其处理的对象均变为二进制文件。
②“r+”:读写,为了读和写打开一个文本文件。“w+”:读写,为了读和写建立一个新的文件。“a+”:读写,打开一个文件,在文件尾进行续写。与之对应的操作二进制文件的打开方式分别为“rb+” “wb+” “ab+”。对于文件不存在的情况均与①中的三个相对应。
FILE* pf=fopen("data.txt","w");
if(pf==NULL)
{perror("fopen");
return 1;}
//...
fclose(pf);
pf=NULL;//文件关闭后最好将文件指针置空
文件的顺序读写
对于文件的具体内容进行操作可以使用以下的文件顺序读写函数。
fgetc:字符输入函数,用以读,适用于所有输入流;fputc:字符输出函数,用以写,适用于所有输出流;
fgets:文本行输入函数,适用于所有输入流;fputs:文本行输出函数,适用于所有输出流;
fscanf:格式化输入函数,适用于所有输入流;fprintf:格式化输出函数,适用于所有输出流;
fread:二进制输入,适用于文件输入流;fwrite:二进制输出,适用于文件输出流。
函数的格式与功能都可以在cplusplus.com - The C++ Resources Network找到,这里不在一一赘述,他们都是在文件中读取或写入数据。值得注意的是,对于不同功能的函数,在打开文件的时候要选择好对应功能的打开方式。
下面实现一个文件拷贝程序,操作的文件是在程序这一文件路径下的两个记事本。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
FILE* pfread = fopen("data.txt", "r");
if (pfread == NULL)
{
perror("fopen->data.txt");
return 1;
}
FILE* pfwrite = fopen("data_copy.txt", "w");
if (pfwrite == NULL)
{
fclose(pfread);
pfread = NULL;
perror("fopen->data_copy.txt");
return 1;
}
int i = 0;
while ((i = fgetc(pfread)) != EOF)
{
fputc(i, pfwrite);
}
fclose(pfread);
fclose(pfwrite);
return 0;
}
文件的随机读写
上面的文件读写函数是按照文件内容顺序依次进行读写,不过这有时并不方便,以下介绍文件的随机读写函数(在指定位置进行读写)。
fseek函数:根据文件指针的位置和偏移量来定义文件指针(文件内容的光标)。举例:
FILE*pf=fopen("data.txt","r");//假设文件中写有“abcdef”
//...
int ch=fgetc(pf);
printf("%c\n",ch);//这时输出的是“a”
int ch=fgetc(pf);
printf("%c\n",ch);//这时输出的是“b”
int ch=fgetc(pf);
printf("%c\n",ch);//这时输出的是“c”
//假设我们要从新回到“a”的位置进行读取
fseek(pf,-3,SEEK_CUR);//从当前位置向前偏移3
/*等效的表示方式还有fseek(pf,-6,SEEK_END);从末尾
和fseek(pf,0,SEEK_SET);从起始位置*/
ftell函数 :返回文件指针相对于起始位置的偏移量。这个函数可以帮助我们检验光标在文件内容中的位置。还可以实现计算文件的整体大小:
fseek(pf,0,SEEK_END);
int n=ftell(pf);
rewind函数:让文件指针的位置回到文件的起始位置。
文件读取结束的判断
feof:当文件读取结束后,判断是否是因为遇到文件末尾而结束;ferror:在文件读取结束后,判断是否是因为遇到错误而结束。
文件文件读取结束后,fgetc判断返回值是否为EOF,fgets判断返回值是否是NULL。ep:
FILE* pf=//...
if(!pf)
{perror("File opening failed");
return EXIT_FAILURE;}
while((c=fgetc(pf))!=EOF)
putchar(c);
if(ferror(pf))
puts("I/O error when reading");
else if(feof(pf))
puts("End of file reached successfully");
fclose(pf);