目录
- C语言文件结束处理
- C++语言文件结束处理
1- C语言文件结束处理
在C语言中,读取一个文件的操作步骤如下
在读取一个的时候,程序是如何知道文件读取结束,并返回的呢?
读取文件的方法有以下:
1、fgetc(FILE*fp)
逐个字符读取文件,当遇到换行符(\r\n或\n)、空格的时候,该函数并不会停止对文件的读取,只有当该函数读取到文件结尾的时候,即读取完文件最后一个字符的时候,才结束对文件的读取,并返回文件结束符(EOF);当读取到换行符时,换行符会保存换行符到数组中。
2、fgets(char *s, int size, FILE*fp)
第一个参数:保存读取文件数据的内存空间(数组等)
第二个参数:将要读取文件size个字节。以换行符(\r\n或\n)或EOF为结束读取文件的标记,当fgets读到这些字符的时候,就会返回并结束对文件的读取。当读取到换行符时,结束读取的同时,换行符会保存换行符到数组中。并且如果数组内存空间足够大,当结束读取文件之后,则还会在数组最后一个字符后面添加一个’\0’字符,反之不添加。
第三个参数:存储各种数据的流。标准流有:stdin,stdout;文件流有自定义的FILE *fp。
(类似逐行读取)
3、fscanf(FILE*fp,"%s.%d…",&a,&b…)
格式化读取文件,这个函数可以类比成scanf的使用。scanf是从标准流中读取数据,而fscanf即能从标准流中读取数据,也能从其他流中读取数据,比如文件流。fscanf 对空格、制表符、换行符是相同对待的,不加区分的;%s会跳过前面的空白符,但是不会跳过后面的空白符;%c不会跳过空白符。当遇到结尾(EOF)时,返回文件结束符(EOF)。
注意:文件结束符并不存在于文件中,我们常说的文件结束符只是一个标记,是不存在文件的,只能简单的判断是否读取文件结束。当读取文件错误时,相关函数也会返回文件结束符(EOF)的。
获取文件大小的方法
方法一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include<stdio> int main() { //打开一个文件 FILE*fp=open("test.txt","rb"); //获取文件大小 int size=0; //将文件指针偏移到文件末尾。即从文件首个字符,偏移到文件最后一个字符的后面(-1)。 fseek(fp,0,SEEK_END); //计算偏移量并返回 size=ftell(fp); //重置文件指针,即将文件指针指向第一个字符 rewind(fp); cout<<"文件大小为:"<<size<<"字节"<<endl; return 0; } |
假如test.txt文件内容如下
123 456
abc
则控制台显示的结果如下
文件大小为:12字节
为什么不是9个字节或者10个字节或者13个字节呢?原因很简单,123到456之间有一个空格符,而456后面有个换行符(\r\n),这两个符号也会计算在内,空格符当做一个字节,\r当做一个字节,\n也是一个字节,但文件结束符(EOF)是不存在于文件中的,EOF也就是不计算在内,所以是12个字节。
再比如,test.txt文件内容如下:
123 456
abc\r\n
则控制台显示的结果如下
文件大小为:16字节
这又是为什么呢?原因是,读取文件的时候,并不会把\r\n当换行符来处理,而是视之为一串字符串来处理,\是一个字符,r和n也是一个字符。所以一共16个字节。(注意文件结束符不计算在内)
声明:以上方式计算文件大小都不会把文件结束符统计到文件大小。
方法二
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include<stdio.h> int main() { //打开一个文件 FILE*fp=open("test.txt","rb"); //获取文件大小 int size=0; char ch; while(feof(fp)==0) { ch=fgetc(fp); size+=1; } cout<<"文件大小为:"<<size<<"字节"<<endl; return 0; } |
假如test.txt文件内容如下
123 456
abc
则控制台显示的结果如下
文件大小为:13字节
咦?为甚是13?而不是12?原因是feof函数在做鬼.
feof(FILE*fp):其功能是检测流上的文件结束符,如果文件结束,则返回非0值,否则返回0(即,文件结束:返回非0值;文件未结束:返回0值)。
注意:feof判断文件结束是通过读取函数fread/fscanf等返回错误来识别的,故而判断文件是否结束应该是在读取函数之后进行判断。比如,在while循环读取一个文件时,如果是在读取函数之前进行判断,则如果文件最后一行是空白行,可能会造成内存错误。
EOF是文本文件结束的标志。在文本文件中,数据是以字符的ASCⅡ代码值的形式存放,普通字符的ASCⅡ代码的范围是32到127(十进制),EOF的16进制代码为0xFF(十进制为-1),因此可以用EOF作为文件结束标志。
当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志。为解决这一个问题,ASCI C提供一个feof函数,用来判断文件是否结束。feof函数既可用以判断二进制文件又可用以判断文本文件。
“C”语言的“feof()”函数和数据库中“eof()”函数的运作是完全不同的。数据库中“eof()”函数读取当前指针的位置,“C”语言的“feof()”函数返回的是最后一次“读操作的内容”。多年来把“位置和内容”相混,从而造成了对这一概念的似是而非。
那么,位置和内容到底有何不同呢?举个简单的例子,比如有人说“你走到火车的最后一节车箱”这就是位置。而如果说“请你一直向后走,摸到铁轨结束”这就是内容。也就是说用内容来判断会“多走一节”。这就是完全依赖于“while(!feof(FP)){…}”进行文件复制时,目标文档总会比源文档“多出一些”的原因。
——引用百度百科
也就是说,在feof前面没有任何读取文件的操作时,无论文件是否为空,feof总是执行多一次判断文件,并这一次总是返回0。它的作用原理,有点类似
再比如,一个文件test.txt内容为空,如下:
则控制台显示的结果如下
文件大小为:1字节
这个字节,就是因为:在feof前面没有任何读取文件的操作时,无论文件是否为空,feof总是执行多一次判断文件,并这一次总是返回0。
假如代码改成如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include<stdio.h> int main() { //打开一个文件 FILE*fp=open("test.txt","rb"); //获取文件大小 int size=0; char ch; ch=fgetc(fp);//在feof前面加上先读取一次文件的操作 while(feof(fp)==0) { ch=fgetc(fp); size+=1; } cout<<"文件大小为:"<<size<<"字节"<<endl; return 0; } |
比如,一个文件test.txt内容为空,如下:
则控制台显示的结果如下
文件大小为:0字节
原因是,在feof前面的fgetc把文件指针先移动到了文件末尾,文件末尾的内容是文件结束符eof(文件结束符并不存在文件中,只是读取一个文件的一个标记符)。所以,feof函数在判断的时候,读取到了文件指针指向的内容为eof,因此返回非0值,文件读取结束。
总结:
1、总的来说,文件大小的获取可以通过偏移量来计算,还可以通过,在读取文件的时候,判断是否读取文件结束,来计数文件大小来获取。在feof前面没有任何读取文件的操作时,feof总是多判断一次文件结束,并这一次总是返回0。原因是,feof判断的是文件的内容,而不是位置。
比如,
在一个数组大小为1的a数组,经过fseek偏移到末尾之后,统计偏移量函数ftell会返回0,因为fseek偏移的是位置,而数组a只有1个内存大小,所以首部位置和尾部位置都是同一个位置,也就是偏移量为0。而feof函数会先判断数组a的内容,是否符合feof返回的条件,符合返回0,不符合,则返回非0.
2、统计文件大小的时候,会把换行符(\r \n或\n),空格符(),制表符(\t)等计算在内
3、
(1)EOF(End of file)是C/C++里面bai的宏定义du,具体定义式是#define EOF -1,表示的是文件的结束标志,值等于-1,一般用在文件读取的dao函数里面,比如fscanf fgetc fgets等,一旦读取到文件最后就返回EOF标志并结束函数调用
(2)’\0’是转义字符,值等于0,主要用在C风格字符串的末尾,表示字符串结束标志。通常用在和字符串相关的函数里面,如strcmp strcpy等会用到它
(3)’\n’表示换行符,通常用作一些读取函数的读取结束标志,比如scanf,getchar(),gets()等,一旦遇到’\n’就结束读取并返回
——引用百度百科
4、当文件打开方式为二进制的时候,换行符会被解析成"\r\n",否则会被解析成"\n"存储。
2- C++语言文件结束处理
在C++语言中,读取一个文件的操作步骤如下
读取文件的方式:
(1)>>:
当遇到换行符或文件结束符时,结束读取文件。换行符不会被存储到数组中,但会从流中取走,下一次再读取的时候,不会读取到该换行符。当读取到文件末尾时,结束对文件的读取,返回EOF。
(2)object.read(char* buffer, int count):
第一参数:指向存储有数据的内存,这块内存将要存储从文件读取的数据。
第二参数:声明将有count个字节会从文件中读取出来,保存到buffer指向的内存中。
说明:当声明读取文件为以二进制模式读取时,换行符会被解析成"\r\n"存储在指定内存中;当不是以二进制读取文件时,换行符会被解析为"\n"存储在指定内存中。
说明:如果想知道read从文件中成功读取了多少个字节,可以使用object.gcount()来获取read函数最近一次成功读取的字节数,来统计从文件读取字节总数。gcount()函数返回的是,read函数最近一次成功从文件读取的字节数。因为读到文件结尾,也未必是读取了count个字节。比如,当count>文件大小时。
该函数会受文件指针的影响,即是从文件指针位置开始读取。与其他c++读取文件函数一样。当读取到文件末尾时,结束对文件的读取。或读取了count个字符后,结束对文件的读取。
(3)object.get(ch):
从文件中读取一个字符,换行符会被解析成"\r\n"存储在指定内存中。当读取到文件末尾时,结束对文件的读取。
(4)getline(char* buf, int bufSize):
第一参数:指向存储有数据的内存,这块内存将要存储从文件读取的数据。
第二参数:声明将有bufSize个字节会从文件中读取出来,保存到buffer指向的内存中。
(1)当遇到换行符时,读取文件结束。
(2)当读取了bufSize个字节后,读取文件结束。
(3)遇到文件结束符时,读取文件结束。
(5)getline(char* buf, int bufSize, char delim):
第一参数:指向存储有数据的内存,这块内存将要存储从文件读取的数据。
第二参数:声明将有bufSize个字节会从文件中读取出来,保存到buffer指向的内存中。
第三参数:声明读取到delim这个字符时,停止对文件的读取,并只保存这个字符之前的数据。
(1)当遇到换行符时,读取文件结束。
(2)当读取了bufSize个字节后,读取文件结束。
(3)遇到文件结束符时,读取文件结束。
(4)当遇到delim这个字符时,读取文件结束。并且把流中的delim字符移除,下一次读取时,不会在遇见当前delim字符。
获取文件大小的方法
方法一
1 2 3 4 5 6 7 8 9 10 11 12 | #include<iostream> #include<fstream> using namespace std; int main() { ifstream infile("test.txt",ios::in); //读 偏移量 infile.seekg(0, ios::end); int len = infile.tellg(); cout << "文件大小为:" << len << "字节" << endl; return 0; } |
test.txt文件内容如下
abc
1 2
控制台显示的内容如下
文件大小为:8字节
方法二
错误演示
1 2 3 4 5 6 7 8 9 10 11 12 | #include<iostream> #include<fstream> using namespace std; int main() { ofstream outfile("test.txt",ios::out); //读 偏移量 infile.seekg(0, ios::end); int len = infile.tellg(); cout << "文件大小为:" << len << "字节" << endl; return 0; } |
test.txt文件内容如下
abc
1 2
控制台显示的内容如下
文件大小为:0字节
为什么会是0呢?原因是,当仅仅以ios::out模式打开文件时,该文件的内容会被清除,所以偏移时,偏移了0个偏移量。
正确演示
1 2 3 4 5 6 7 8 9 10 11 12 | #include<iostream> #include<fstream> using namespace std; int main() { ofstream outfile("test.txt",ios::out|ios::in); //读 偏移量 infile.seekg(0, ios::end); int len = infile.tellg(); cout << "文件大小为:" << len << "字节" << endl; return 0; } |
test.txt文件内容如下
abc
1 2
控制台显示的内容如下
文件大小为:8字节
只需添加上ios::in只读即可.
方法三
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include<iostream> #include<fstream> using namespace std; int main() { ifstream infile("test.txt",ios::in); //读数据的过程中统计读取数据的字节个数 char ch; int len=0; while(infile.get(ch)) cout << "文件大小为:" << len << "字节" << endl; return 0; } |
test.txt文件内容如下
abc
1 2
控制台显示的内容如下
文件大小为:7字节
二进制打开文件的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include<iostream> #include<fstream> using namespace std; int main() { ifstream infile("test.txt",ios::in|ios::binary); //读数据的过程中统计读取数据的字节个数 char ch; int len=0; while(infile.get(ch)) cout << "文件大小为:" << len << "字节" << endl; return 0; } |
test.txt文件内容如下
abc
1 2
控制台显示的内容如下
文件大小为:8字节
为什么一个是7一个是8呢?因为以二进制打开文件读的时候,换行符会被解析成"\r\n",反之会被解析成"\n"。
总结
1、获取文件大小的方式有三种,一种是读偏移量计算,一种是写偏移量计算,还有一种读文件数据统计。
2、当文件打开方式为二进制的时候,换行符会被解析成"\r\n",否则会被解析成"\n"存储。
大总结
1、无论是c还是c++,当文件打开方式为二进制的时候,换行符会被解析成"\r\n",否则会被解析成"\n"存储。
2、偏移量计算文件大小的时候,换行符会被解析成"\r\n";不是二进制打开的文件,读取文件,并统计的方式计算文件大小,换行符会被解析成"\n";以二进制打开的文件,读取文件,并统计的方式计算文件大小,换行符会被解析成"\r\n"。
3、c语言中
(1)遇见EOF就停止读取文件的函数是:fgetc,fgets,fscanf
(2)遇见换行符(\r\n或\n)就停止读取文件的函数是:fgets
4、在c++语言中
(1)遇见EOF就停止读取文件的函数是:get,read,>>
(2)遇见换行符(\r\n或\n)就停止读取文件的函数是:>>
5、c语言写文件的函数:fputc,fputs,fwrite,fprintf
6、c语言读文件的函数:fgetc,fgets,fread,fscanf
7、c++语言写文件的方法:<<,put,write
8、c++语言读文件的方法:>>,get,getline,read