- 浏览: 157771 次
- 性别:
- 来自: 深圳
-
文章分类
最新评论
-
lyaqys:
lz实现的OptimisticExclusiveLock有点问 ...
java park/unpark 【java并发】基于JUC CAS原理,自己实现简单独占锁
>> 关于文件结束符EOF
EOF 是 End Of File 的缩写。
在C语言中,它是在标准库中定义的一个宏。
人们经常误认为 EOF 是从文件中读取的一个字符(牢记)。其实,EOF 不是一个字符,它被定义为是 int 类型的一个负数(比如 -1)。EOF 也不是文件中实际存在的内容。EOF 也不是只表示读文件到了结尾这一状态(这种状态可以用 feof() 来检测),它还能表示 I/O 操作中的读、写错误(通常可以用 ferror() 来检测)以及其它一些关联操作的错误状态。
一、getchar的两点总结:
1.getchar是以行为单位进行存取的。
当用getchar进行输入时,如果输入的第一个字符为有效字符(即输入不是文件结束符EOF,Windows下为组合键Ctrl+Z,Unix/Linux下为组合键Ctrl+D),那么只有当最后一个输入字符为换行符'/n'(也可以是文件结束符EOF,EOF将在后面讨论)时,getchar才会停止执行,整个程序将会往下执行。譬如下面程序段:
while((c =getchar())!=EOF){
putchar(c);
}
执行程序,输入:abc,然后回车。则程序就会去执行puchar(c),然后输出abc,这个地方不要忘了,系统输出的还有一个回车。然后可以继续输入,再次遇到换行符的时候,程序又会把那一行的输入的字符输出在终端上。
对于getchar,肯定很多初学的朋友会问,getchar不是以字符为单位读取的吗?那么,既然我输入了第一个字符a,肯定满足while循环(c = getchar()) != EOF的条件阿,那么应该执行putchar(c)在终端输出一个字符a。不错,我在用getchar的时候也是一直这么想的,但是程序就偏偏不着样执行,而是必需读到一个换行符或者文件结束符EOF才进行一次输出。对这个问题的一个解释是,在大师编写C的时候,当时并没有所谓终端输入的概念,所有的输入实际上都是按照文件进行读取的,文件中一般都是以行为单位的。因此,只有遇到换行符,那么程序会认为输入结束,然后采取执行程序的其他部分。同时,输入是按照文件的方式存取的,那么要结束一个文件的输入就需用到EOF(Enf Of File). 这也就是为什么getchar结束输入退出时要用EOF的原因。
2.getchar()的返回值一般情况下是字符,但也可能是负值,即返回EOF。
这里要强调的一点就是,getchar函数通常返回终端所输入的字符,这些字符系统中对应的ASCII值都是非负的。因此,很多时候,我们会写这样的两行代码:
char c;
c =getchar();
这样就很有可能出现问题。因为getchar函数除了返回终端输入的字符外,在遇到Ctrl+D(Linux下)即文件结束符EOF时,getchar()的返回EOF,这个EOF在函数库里一般定义为-1。因此,在这种情况下,getchar函数返回一个负值,把一个负值赋给一个char型的变量是不正确的。为了能够让所定义的变量能够包含getchar函数返回的所有可能的值,正确的定义方法如下(K&R C中特别提到了这个问题):
int c;
c =getchar();
二、EOF的两点总结(主要指普通终端中的EOF)
1.EOF作为文件结束符时的情况:
EOF虽然是文件结束符,但并不是在任何情况下输入Ctrl+D(Windows下Ctrl+Z)都能够实现文件结束的功能,只有在下列的条件下,才作为文件结束符。
(1)遇到getcahr函数执行时,要输入第一个字符时就直接输入Ctrl+D,就可以跳出getchar(),去执行程序的其他部分;
(2)在前面输入的字符为换行符时,接着输入Ctrl+D;
(3)在前面有字符输入且不为换行符时,要连着输入两次Ctrl+D,这时第二次输入的Ctrl+D起到文件结束符的功能,至于第一次的Ctrl+D的作用将在下面介绍。
其实,这三种情况都可以总结为只有在getchar()提示新的一次输入时,直接输入Ctrl+D才相当于文件结束符。
2.EOF作为行结束符时的情况,这时候输入Ctrl+D并不能结束getchar(),而只能引发getchar()提示下一轮的输入。
这种情况主要是在进行getchar()新的一行输入时,当输入了若干字符(不能包含换行符)之后,直接输入Ctrl+D,此时的Ctrl+D并不是文件结束符,而只是相当于换行符的功能,即结束当前的输入。以上面的代码段为例,如果执行时输入abc,然后Ctrl+D,程序输出结果为:
abcabc
注意:第一组abc为从终端输入的,然后输入Ctrl+D,就输出第二组abc,同时光标停在第二组字符的c后面,然后可以进行新一次的输入。这时如果再次输入Ctrl+D,则起到了文件结束符的作用,结束getchar()。
如果输入abc之后,然后回车,输入换行符的话,则终端显示为:
abc //第一行,带回车
abc //第二行
//第三行
其中第一行为终端输入,第二行为终端输出,光标停在了第三行处,等待新一次的终端输入。
从这里也可以看出Ctrl+D和换行符分别作为行结束符时,输出的不同结果。
EOF的作用也可以总结为:当终端有字符输入时,Ctrl+D产生的EOF相当于结束本行的输入,将引起getchar()新一轮的输入;当终端没有字符输入或者可以说当getchar()读取新的一次输入时,输入Ctrl+D,此时产生的EOF相当于文件结束符,程序将结束getchar()的执行。
【补充】本文第二部分中关于EOF的总结部分,适用于终端驱动处于一次一行的模式下。也就是虽然getchar()和putchar()确实是按照每次一个字符 进行的。但是终端驱动处于一次一行的模式,它的输入只有到“/n”或者EOF时才结束,因此,终端上得到的输出也都是按行的。
如果要实现终端在读一个字符就结束输入的话,下面的程序是一种实现的方法(参考《C专家编程》,略有改动)
/*Edit by Godbach
CU Blog: http://blog.chinaunix.net/u/33048/
*/
#include<stdio.h>
#include<stdlib.h>
int
main(void)
{
int c;
/* 终端驱动处于普通的一次一行模式 */
system("stty raw");
/* 现在的终端驱动处于一次一个字符模式 */
c =getchar();
putchar();
/* 终端驱动处又回到一次一行模式 */
system("stty cooked");
return 0;
}
编译运行该程序,则当如入一个字符时,直接出处一个字符,然后程序结束。
由此可见,由于终端驱动的模式不同,造成了getchar()输入结束的条件不一样。普通模式下需要回车或者EOF,而在一次一个字符的模式下,则输入一个字符之后就结束了。
(1) 字节的读取
在正常的情况下, getc 以 unsigned char 的方式读取文件流, 扩张为一个整数,并返
回. 换言之, getc 从文件流中取一个字节, 并加上24个零,成为一个小于256的整数,
然后返回.
int c;
while ((c = fgetc (rfp))!= -1) // -1就是 EOF
fputc (c, wfp);
上面 fputc 中的 c 虽然是整数, 但在 fputc 将其写入文件流之前, 又把整数的高24位
去掉了, 因此 fgetc, putc 配合能够实现文件复制. 到目前为止, 把 c 定义为
char仍然是可行的, 但下面我们将看到,把 c 定义为 int 是为正确判段文件是否结束.
(2) 判断文件结束.
多数人认为文件中有一个EOF,用于表示文件的结尾. 但这个观点实际上是错误的,在文
件所包含的数据中,并没有什么文件结束符. 对getc 而言, 如果不能从文件中读取,
则返回一个整数 -1,这就是所谓的EOF. 返回 EOF 无非是出现了两种情况,一是文件已
经读完; 二是文件读取出错,反正是读不下去了.
请注意: 在正常读取的情况下, 返回的整数均小于256, 即0x0~0xFF. 而读不出返回的
是 0xFFFFFFFF. 但, 假如你用fputc把 0xFFFFFFFF 往文件里头写, 高24位被屏蔽,写入的将
是 0xFF. // lixforalpha 请注意这一点
(3) 0xFF 会使我们混淆吗?
不会, 前提是, 接收返回值的 c 要按原型定义为 int.
如果下一个读取的字符将为 0xFF, 则
int c;
c = fgetc (rfp); // c = 0x000000FF;
if (c != -1) // 当然不等, -1 是 0xFFFFFFFF
fputc (wfp); // 噢, OXFF 复制成功.
字符0xFF, 其本身并不是EOF.
(4) 将 c 定义 char
假定下一个读取的字符为 0xFF 则
char c;
c = fgetc (rfp); // fgetc(rfp)的值为 0x000000FF, 暗中降为字节, c = 0xFF
if (c != -1) // 字符与整数比较? c 被带符号(signed)扩展为0xFFFFFFFF, 喔噢,
条件成立,文件复制提前退出.
while ((c=fgetc(rfp))!=EOF) 中的判别条件成立, 文件复制结束! 意外中止.
(5) 将 c 定义为 unsigned char;
当读到文件末尾, 返回 EOF 也就是 -1 时,
unsigned char c;
c = fgetc (rfp); // fgetc (rfp)的值为EOF,即-1,即0xFFFFFFFF, 降格为字节, c=0xFF
if ( c!= -1) // c 被扩展为 0x000000FF, 永远不回等于 0xFFFFFFFF
所以这次虽然能正确复制 0xFF, 但却不能判断文件结束. 事实上,在 c 为 uchar 时,
c != -1 是永远成立的, 一个高质量的编译器, 比如 gcc会在编译时指出这一点.
(6) 为何需要feof?
FILE *fp;
fp 指向一个很复杂的数据结构, feof 是通过这个结构中的标志来判断文件是否结束的.
如果文件用 fgetc 读取, 刚好把最后一个字符读出时, fp 中的EOF标志不会打开,这时
用feof判断,将会得到文件尚未结束的结论.
fgetc 返回 -1 时, 我们仍无法确信文件已经结束, 因为可能是读取错误! 这时我们
需要 feof 和 ferror.
总结:EOF并不是存在于文件中的,而是一种状态,当读到文件末尾或者读取出错时就会返回这个值来判断文件结束。(即即使读取错误可能也被认为文件结束,所以就需要用feof 和 ferror来判断是不是真的文件结束了)
当用getchar(c)时,即使c定义成字符型,也可以结束,主要是c与-1比较时,c也会从char转换为整型值。
写个小程序验证了一下
[cpp] view plaincopy
#include <stdio.h>
int main()
{
char c;
c = -1;
printf("%x",c);
return 0;
}
得到的结果为ffffffff,所以c即使定义为char型,读取文件等时还是能正常结束。
关于EOF(文件结束符)问题的体会
最近写了些代码,在对文件的操作中发现了很经典的EOF问题,呵呵。
EOF,即end of file,文件结尾,作为文件结束的标志,在程序中常作为判断的一个标志。但在我们平常的程序中却常发生意想不到的结果。
下面这段程序,猜猜它输出的是什么?
char c;
ifstream fin("d://dat");//设d:/dat文件已存在,内容为ab。
while(!fin.eof())
{
fin >> c;
cout << c;
}
输出结果是abb,没想到吗?你可能会问,再输出第一个b的时候,文件指针已经指向了EOF,为何不结束?
问题的关键是文件EOF机制是怎样运作的。
我们来谈三个问题:
1、文件指针
当打开一个文件时,文件指针位置为0,并不是指向第一个字符,即第一个字符的位置为1。这一点我们可以通过peek()函数验证。peek()返回的是当前文件指针下一个位置的字符。所以有:
ofstream fo("d://dat");
fo << 'h';
fo.close();
ifstream fi("d://dat");
char temp = fi.peek();
cout << temp;
会显示h。
还有,用fo.seekp(0,ios::beg),得文件指针为0;fo.seekp(0,ios::end),得文件指针指向最后一个字符。
2、关于EOF
很多朋友认为文件尾有EOF,这是错误的。EOF是流的状态标志。在 C++中,是在读取文件失败时才产生EOF。所以第一个程序中,在输出第一个b时,产生了EOF,再输出第二个b时读取到EOF,循环结束。
3、解决EOF困惑的办法
我感觉在判断文件结束上,最好的方法就是判断文件指针相对于开头的位置,是否等于文件长度。即:
long filelen;
ifstream fin("d://dat");//设d:/dat文件已存在,内容为ab。
fin.seekg(0,ios::end);
filelen = fin.tellg();//获取文件长度
fin.seekg(0,ios::beg);
while (1)
{
if (filelen == fin.tellg())//到达文件尾,即指向EOF
{
flag = true;
break;
}
读取数据...
}
当然还有别的方法,就是用peek()的预读性。
peek()返回当前文件指针下一个位置的字符,而指针位置不变。所以我们可以这样:
while (fi.peel()!=EOF)
{
...
}
当while循环体中,文件指针指向最后一个字符,若没有fi.peel()!=EOF,则需要再下一个循环中才能触发EOF。而加了fi.peel()!=EOF后,用预读的方法检测出了EOF。呵呵,这个方法挺好的吧!
ifstream 流 判断文件是否结尾的函数eof()
分类: C++2009-10-14 14:48 1807人阅读 评论(2) 收藏 举报
fstream流的eof() 判断有点不合常理
按正常逻辑来说,如果到了文件末尾的话 ,那eof()应返回真
但是,c++输入输出流如何知道是否到末尾呢?
原来是根据的是: 如果fin>>不能再读入数据了,才发现到了文件结尾,这时才给流设定文件结尾的标志,此后调用eof()时,才返回真。
假设
fin>>x; //此时文件刚好读完最后一个数据(将其保存在x中)
但是, 这时 fin.eof()仍未假 因为,fin流的标志eofbit是FALSE, fin流此时认为文件还没有到末尾
只有当流再次读写时
fin>>x; 发现已无可读写数据,此时流才知道到达了结尾,这时才将标志eofbit修改为TRUE
此时流才知道了文件到底了末尾
也就是说,eof在读取完最后一个数据后,仍是False,
当再次试图读一个数据时,由于发现没数据可读了 才知道到末尾了,此时才修改标志,eof变为TRUE
以下例子:
ifstream fin("D://line.txt");
ofstream fout("D://T_line.txt",ios::trunc);
list<tag_Point> test_list;
tag_Point test;
while (!fin.eof())
{
fin>>test.x;
fin>>test.y;
fin>>test.z;
test_list.push_back(test);
}
fin.close();
在运行时 发现 test_list中的数据比文本中的数据多一行,也就是 文本中最后一行的数据写了两遍
始终无法理解
现在明白了:》
再读完最后一行后,
因为fin.eof()仍为假, 所以会继续while循环
当执行到while的第一个语句 fin>>test.x时,发现无可读数据了,此时修改流属性,fin.eof ()变为TRUE
再执行 fin>>test.y; fin>>test.z;时,因为已经到文件末尾了 ,所以 test保留了上次的值,也即test中的值为变,还是文本最后一行
的数据
此时再push_back(test),压入列表的仍是最后一行数据
由此导致了,列表中的数据比文本中的数据多一行
---------------------
知道了原因 ,便很好作出修改了
修改为:
while ( fin>>test.x&&fin>>test.y&& fin>>test.z)
{
test_list.push_back(test);
}
fin.close();
这样便没问题了 ,当读取完最后一行数据后,将其放入列表中,此时判断while条件,也就是再次读取数据,发现无数据可读,读取不成功 fin>>test.x返回False 由此结束循环。
C++编程语言中的很多功能在我们的实际应用中起着非常大的作用。比如在对文件文本的操作上,就可以用多种方式来实现。在这里我们介绍的C++ eof()函数就是其中一个比较常用的基本函数。
在使用C/C++读文件的时候,一定都使用过C++ eof()函数来判断文件是否为空或者是否读到文件结尾了,也会在使用这个函数的过程中遇到一些问题,如不能准确的判断是否为空或者是否到了文件尾,以至于有些人可能还会怀疑这个函数是不是本身在设计上就有问题。
先来看看如下这段代码:
#include < iostream>
#include < fstream>
using namespace std;
int main()
{
char ch = 'x';
ifstream fin("test.txt" /*, ios::binary*/);
if (fin.eof())
{
cout < < "file is empty."< < endl;
return 0;
}
while (!fin.eof())
{
fin.get(ch);
cout < < ch;
}
system("pause");
return 0;
}
如果test.txt不存在,程序会形成死循环,fin.eof()永远返回false,就是说,eof在读取完最后一个数据后,仍是False,当再次试图读一个数据时,由于发现没数据可读了 才知道到末尾了,此时才修改标志,eof变为TRUE
如果test.txt为空,程序打印出一个x字符,因为循环刚进来时eof()状态还没设置,当读不到数据时设置为ture循环结束;
当test.txt中存在一字符串“abcd”且没有换行时,程序打印出“abcdd”,
当存在以上字符串并且有一新的空行时,程序打印出“abcd”加上一空行。其实是两行 oA输出了两次,显示调试器的caret在第三行,为什么没OD了因为是以文本方式打开的 odoa自动转化为oa;
这种现象可能让很多人很迷惑,程序运行的结果似乎很不稳定,时对时错。使用binary模式读时结果一样。在这里,大家可能有一个误区,认为eof()返回true时是读到文件的最后一个字符,其实不然,C++ eof()函数返回true时是读到文件结束符0xFF,而文件结束符是最后一个字符的下一个字符。
while(infile.peek()!=EOF) 好处在于他获取的下一个数据(但并不会让FILE指针++哦)
EOF 是 End Of File 的缩写。
在C语言中,它是在标准库中定义的一个宏。
人们经常误认为 EOF 是从文件中读取的一个字符(牢记)。其实,EOF 不是一个字符,它被定义为是 int 类型的一个负数(比如 -1)。EOF 也不是文件中实际存在的内容。EOF 也不是只表示读文件到了结尾这一状态(这种状态可以用 feof() 来检测),它还能表示 I/O 操作中的读、写错误(通常可以用 ferror() 来检测)以及其它一些关联操作的错误状态。
一、getchar的两点总结:
1.getchar是以行为单位进行存取的。
当用getchar进行输入时,如果输入的第一个字符为有效字符(即输入不是文件结束符EOF,Windows下为组合键Ctrl+Z,Unix/Linux下为组合键Ctrl+D),那么只有当最后一个输入字符为换行符'/n'(也可以是文件结束符EOF,EOF将在后面讨论)时,getchar才会停止执行,整个程序将会往下执行。譬如下面程序段:
while((c =getchar())!=EOF){
putchar(c);
}
执行程序,输入:abc,然后回车。则程序就会去执行puchar(c),然后输出abc,这个地方不要忘了,系统输出的还有一个回车。然后可以继续输入,再次遇到换行符的时候,程序又会把那一行的输入的字符输出在终端上。
对于getchar,肯定很多初学的朋友会问,getchar不是以字符为单位读取的吗?那么,既然我输入了第一个字符a,肯定满足while循环(c = getchar()) != EOF的条件阿,那么应该执行putchar(c)在终端输出一个字符a。不错,我在用getchar的时候也是一直这么想的,但是程序就偏偏不着样执行,而是必需读到一个换行符或者文件结束符EOF才进行一次输出。对这个问题的一个解释是,在大师编写C的时候,当时并没有所谓终端输入的概念,所有的输入实际上都是按照文件进行读取的,文件中一般都是以行为单位的。因此,只有遇到换行符,那么程序会认为输入结束,然后采取执行程序的其他部分。同时,输入是按照文件的方式存取的,那么要结束一个文件的输入就需用到EOF(Enf Of File). 这也就是为什么getchar结束输入退出时要用EOF的原因。
2.getchar()的返回值一般情况下是字符,但也可能是负值,即返回EOF。
这里要强调的一点就是,getchar函数通常返回终端所输入的字符,这些字符系统中对应的ASCII值都是非负的。因此,很多时候,我们会写这样的两行代码:
char c;
c =getchar();
这样就很有可能出现问题。因为getchar函数除了返回终端输入的字符外,在遇到Ctrl+D(Linux下)即文件结束符EOF时,getchar()的返回EOF,这个EOF在函数库里一般定义为-1。因此,在这种情况下,getchar函数返回一个负值,把一个负值赋给一个char型的变量是不正确的。为了能够让所定义的变量能够包含getchar函数返回的所有可能的值,正确的定义方法如下(K&R C中特别提到了这个问题):
int c;
c =getchar();
二、EOF的两点总结(主要指普通终端中的EOF)
1.EOF作为文件结束符时的情况:
EOF虽然是文件结束符,但并不是在任何情况下输入Ctrl+D(Windows下Ctrl+Z)都能够实现文件结束的功能,只有在下列的条件下,才作为文件结束符。
(1)遇到getcahr函数执行时,要输入第一个字符时就直接输入Ctrl+D,就可以跳出getchar(),去执行程序的其他部分;
(2)在前面输入的字符为换行符时,接着输入Ctrl+D;
(3)在前面有字符输入且不为换行符时,要连着输入两次Ctrl+D,这时第二次输入的Ctrl+D起到文件结束符的功能,至于第一次的Ctrl+D的作用将在下面介绍。
其实,这三种情况都可以总结为只有在getchar()提示新的一次输入时,直接输入Ctrl+D才相当于文件结束符。
2.EOF作为行结束符时的情况,这时候输入Ctrl+D并不能结束getchar(),而只能引发getchar()提示下一轮的输入。
这种情况主要是在进行getchar()新的一行输入时,当输入了若干字符(不能包含换行符)之后,直接输入Ctrl+D,此时的Ctrl+D并不是文件结束符,而只是相当于换行符的功能,即结束当前的输入。以上面的代码段为例,如果执行时输入abc,然后Ctrl+D,程序输出结果为:
abcabc
注意:第一组abc为从终端输入的,然后输入Ctrl+D,就输出第二组abc,同时光标停在第二组字符的c后面,然后可以进行新一次的输入。这时如果再次输入Ctrl+D,则起到了文件结束符的作用,结束getchar()。
如果输入abc之后,然后回车,输入换行符的话,则终端显示为:
abc //第一行,带回车
abc //第二行
//第三行
其中第一行为终端输入,第二行为终端输出,光标停在了第三行处,等待新一次的终端输入。
从这里也可以看出Ctrl+D和换行符分别作为行结束符时,输出的不同结果。
EOF的作用也可以总结为:当终端有字符输入时,Ctrl+D产生的EOF相当于结束本行的输入,将引起getchar()新一轮的输入;当终端没有字符输入或者可以说当getchar()读取新的一次输入时,输入Ctrl+D,此时产生的EOF相当于文件结束符,程序将结束getchar()的执行。
【补充】本文第二部分中关于EOF的总结部分,适用于终端驱动处于一次一行的模式下。也就是虽然getchar()和putchar()确实是按照每次一个字符 进行的。但是终端驱动处于一次一行的模式,它的输入只有到“/n”或者EOF时才结束,因此,终端上得到的输出也都是按行的。
如果要实现终端在读一个字符就结束输入的话,下面的程序是一种实现的方法(参考《C专家编程》,略有改动)
/*Edit by Godbach
CU Blog: http://blog.chinaunix.net/u/33048/
*/
#include<stdio.h>
#include<stdlib.h>
int
main(void)
{
int c;
/* 终端驱动处于普通的一次一行模式 */
system("stty raw");
/* 现在的终端驱动处于一次一个字符模式 */
c =getchar();
putchar();
/* 终端驱动处又回到一次一行模式 */
system("stty cooked");
return 0;
}
编译运行该程序,则当如入一个字符时,直接出处一个字符,然后程序结束。
由此可见,由于终端驱动的模式不同,造成了getchar()输入结束的条件不一样。普通模式下需要回车或者EOF,而在一次一个字符的模式下,则输入一个字符之后就结束了。
(1) 字节的读取
在正常的情况下, getc 以 unsigned char 的方式读取文件流, 扩张为一个整数,并返
回. 换言之, getc 从文件流中取一个字节, 并加上24个零,成为一个小于256的整数,
然后返回.
int c;
while ((c = fgetc (rfp))!= -1) // -1就是 EOF
fputc (c, wfp);
上面 fputc 中的 c 虽然是整数, 但在 fputc 将其写入文件流之前, 又把整数的高24位
去掉了, 因此 fgetc, putc 配合能够实现文件复制. 到目前为止, 把 c 定义为
char仍然是可行的, 但下面我们将看到,把 c 定义为 int 是为正确判段文件是否结束.
(2) 判断文件结束.
多数人认为文件中有一个EOF,用于表示文件的结尾. 但这个观点实际上是错误的,在文
件所包含的数据中,并没有什么文件结束符. 对getc 而言, 如果不能从文件中读取,
则返回一个整数 -1,这就是所谓的EOF. 返回 EOF 无非是出现了两种情况,一是文件已
经读完; 二是文件读取出错,反正是读不下去了.
请注意: 在正常读取的情况下, 返回的整数均小于256, 即0x0~0xFF. 而读不出返回的
是 0xFFFFFFFF. 但, 假如你用fputc把 0xFFFFFFFF 往文件里头写, 高24位被屏蔽,写入的将
是 0xFF. // lixforalpha 请注意这一点
(3) 0xFF 会使我们混淆吗?
不会, 前提是, 接收返回值的 c 要按原型定义为 int.
如果下一个读取的字符将为 0xFF, 则
int c;
c = fgetc (rfp); // c = 0x000000FF;
if (c != -1) // 当然不等, -1 是 0xFFFFFFFF
fputc (wfp); // 噢, OXFF 复制成功.
字符0xFF, 其本身并不是EOF.
(4) 将 c 定义 char
假定下一个读取的字符为 0xFF 则
char c;
c = fgetc (rfp); // fgetc(rfp)的值为 0x000000FF, 暗中降为字节, c = 0xFF
if (c != -1) // 字符与整数比较? c 被带符号(signed)扩展为0xFFFFFFFF, 喔噢,
条件成立,文件复制提前退出.
while ((c=fgetc(rfp))!=EOF) 中的判别条件成立, 文件复制结束! 意外中止.
(5) 将 c 定义为 unsigned char;
当读到文件末尾, 返回 EOF 也就是 -1 时,
unsigned char c;
c = fgetc (rfp); // fgetc (rfp)的值为EOF,即-1,即0xFFFFFFFF, 降格为字节, c=0xFF
if ( c!= -1) // c 被扩展为 0x000000FF, 永远不回等于 0xFFFFFFFF
所以这次虽然能正确复制 0xFF, 但却不能判断文件结束. 事实上,在 c 为 uchar 时,
c != -1 是永远成立的, 一个高质量的编译器, 比如 gcc会在编译时指出这一点.
(6) 为何需要feof?
FILE *fp;
fp 指向一个很复杂的数据结构, feof 是通过这个结构中的标志来判断文件是否结束的.
如果文件用 fgetc 读取, 刚好把最后一个字符读出时, fp 中的EOF标志不会打开,这时
用feof判断,将会得到文件尚未结束的结论.
fgetc 返回 -1 时, 我们仍无法确信文件已经结束, 因为可能是读取错误! 这时我们
需要 feof 和 ferror.
总结:EOF并不是存在于文件中的,而是一种状态,当读到文件末尾或者读取出错时就会返回这个值来判断文件结束。(即即使读取错误可能也被认为文件结束,所以就需要用feof 和 ferror来判断是不是真的文件结束了)
当用getchar(c)时,即使c定义成字符型,也可以结束,主要是c与-1比较时,c也会从char转换为整型值。
写个小程序验证了一下
[cpp] view plaincopy
#include <stdio.h>
int main()
{
char c;
c = -1;
printf("%x",c);
return 0;
}
得到的结果为ffffffff,所以c即使定义为char型,读取文件等时还是能正常结束。
关于EOF(文件结束符)问题的体会
最近写了些代码,在对文件的操作中发现了很经典的EOF问题,呵呵。
EOF,即end of file,文件结尾,作为文件结束的标志,在程序中常作为判断的一个标志。但在我们平常的程序中却常发生意想不到的结果。
下面这段程序,猜猜它输出的是什么?
char c;
ifstream fin("d://dat");//设d:/dat文件已存在,内容为ab。
while(!fin.eof())
{
fin >> c;
cout << c;
}
输出结果是abb,没想到吗?你可能会问,再输出第一个b的时候,文件指针已经指向了EOF,为何不结束?
问题的关键是文件EOF机制是怎样运作的。
我们来谈三个问题:
1、文件指针
当打开一个文件时,文件指针位置为0,并不是指向第一个字符,即第一个字符的位置为1。这一点我们可以通过peek()函数验证。peek()返回的是当前文件指针下一个位置的字符。所以有:
ofstream fo("d://dat");
fo << 'h';
fo.close();
ifstream fi("d://dat");
char temp = fi.peek();
cout << temp;
会显示h。
还有,用fo.seekp(0,ios::beg),得文件指针为0;fo.seekp(0,ios::end),得文件指针指向最后一个字符。
2、关于EOF
很多朋友认为文件尾有EOF,这是错误的。EOF是流的状态标志。在 C++中,是在读取文件失败时才产生EOF。所以第一个程序中,在输出第一个b时,产生了EOF,再输出第二个b时读取到EOF,循环结束。
3、解决EOF困惑的办法
我感觉在判断文件结束上,最好的方法就是判断文件指针相对于开头的位置,是否等于文件长度。即:
long filelen;
ifstream fin("d://dat");//设d:/dat文件已存在,内容为ab。
fin.seekg(0,ios::end);
filelen = fin.tellg();//获取文件长度
fin.seekg(0,ios::beg);
while (1)
{
if (filelen == fin.tellg())//到达文件尾,即指向EOF
{
flag = true;
break;
}
读取数据...
}
当然还有别的方法,就是用peek()的预读性。
peek()返回当前文件指针下一个位置的字符,而指针位置不变。所以我们可以这样:
while (fi.peel()!=EOF)
{
...
}
当while循环体中,文件指针指向最后一个字符,若没有fi.peel()!=EOF,则需要再下一个循环中才能触发EOF。而加了fi.peel()!=EOF后,用预读的方法检测出了EOF。呵呵,这个方法挺好的吧!
ifstream 流 判断文件是否结尾的函数eof()
分类: C++2009-10-14 14:48 1807人阅读 评论(2) 收藏 举报
fstream流的eof() 判断有点不合常理
按正常逻辑来说,如果到了文件末尾的话 ,那eof()应返回真
但是,c++输入输出流如何知道是否到末尾呢?
原来是根据的是: 如果fin>>不能再读入数据了,才发现到了文件结尾,这时才给流设定文件结尾的标志,此后调用eof()时,才返回真。
假设
fin>>x; //此时文件刚好读完最后一个数据(将其保存在x中)
但是, 这时 fin.eof()仍未假 因为,fin流的标志eofbit是FALSE, fin流此时认为文件还没有到末尾
只有当流再次读写时
fin>>x; 发现已无可读写数据,此时流才知道到达了结尾,这时才将标志eofbit修改为TRUE
此时流才知道了文件到底了末尾
也就是说,eof在读取完最后一个数据后,仍是False,
当再次试图读一个数据时,由于发现没数据可读了 才知道到末尾了,此时才修改标志,eof变为TRUE
以下例子:
ifstream fin("D://line.txt");
ofstream fout("D://T_line.txt",ios::trunc);
list<tag_Point> test_list;
tag_Point test;
while (!fin.eof())
{
fin>>test.x;
fin>>test.y;
fin>>test.z;
test_list.push_back(test);
}
fin.close();
在运行时 发现 test_list中的数据比文本中的数据多一行,也就是 文本中最后一行的数据写了两遍
始终无法理解
现在明白了:》
再读完最后一行后,
因为fin.eof()仍为假, 所以会继续while循环
当执行到while的第一个语句 fin>>test.x时,发现无可读数据了,此时修改流属性,fin.eof ()变为TRUE
再执行 fin>>test.y; fin>>test.z;时,因为已经到文件末尾了 ,所以 test保留了上次的值,也即test中的值为变,还是文本最后一行
的数据
此时再push_back(test),压入列表的仍是最后一行数据
由此导致了,列表中的数据比文本中的数据多一行
---------------------
知道了原因 ,便很好作出修改了
修改为:
while ( fin>>test.x&&fin>>test.y&& fin>>test.z)
{
test_list.push_back(test);
}
fin.close();
这样便没问题了 ,当读取完最后一行数据后,将其放入列表中,此时判断while条件,也就是再次读取数据,发现无数据可读,读取不成功 fin>>test.x返回False 由此结束循环。
C++编程语言中的很多功能在我们的实际应用中起着非常大的作用。比如在对文件文本的操作上,就可以用多种方式来实现。在这里我们介绍的C++ eof()函数就是其中一个比较常用的基本函数。
在使用C/C++读文件的时候,一定都使用过C++ eof()函数来判断文件是否为空或者是否读到文件结尾了,也会在使用这个函数的过程中遇到一些问题,如不能准确的判断是否为空或者是否到了文件尾,以至于有些人可能还会怀疑这个函数是不是本身在设计上就有问题。
先来看看如下这段代码:
#include < iostream>
#include < fstream>
using namespace std;
int main()
{
char ch = 'x';
ifstream fin("test.txt" /*, ios::binary*/);
if (fin.eof())
{
cout < < "file is empty."< < endl;
return 0;
}
while (!fin.eof())
{
fin.get(ch);
cout < < ch;
}
system("pause");
return 0;
}
如果test.txt不存在,程序会形成死循环,fin.eof()永远返回false,就是说,eof在读取完最后一个数据后,仍是False,当再次试图读一个数据时,由于发现没数据可读了 才知道到末尾了,此时才修改标志,eof变为TRUE
如果test.txt为空,程序打印出一个x字符,因为循环刚进来时eof()状态还没设置,当读不到数据时设置为ture循环结束;
当test.txt中存在一字符串“abcd”且没有换行时,程序打印出“abcdd”,
当存在以上字符串并且有一新的空行时,程序打印出“abcd”加上一空行。其实是两行 oA输出了两次,显示调试器的caret在第三行,为什么没OD了因为是以文本方式打开的 odoa自动转化为oa;
这种现象可能让很多人很迷惑,程序运行的结果似乎很不稳定,时对时错。使用binary模式读时结果一样。在这里,大家可能有一个误区,认为eof()返回true时是读到文件的最后一个字符,其实不然,C++ eof()函数返回true时是读到文件结束符0xFF,而文件结束符是最后一个字符的下一个字符。
while(infile.peek()!=EOF) 好处在于他获取的下一个数据(但并不会让FILE指针++哦)
发表评论
-
c语言链表实现学生管理
2013-10-28 14:13 917#include<stdio.h> #includ ... -
简单的linux -c http-client
2013-10-23 15:35 4783#include<stdio.h> #includ ... -
毗连“"aa"”和“"bb"”不能给出一个有效的预处理标识符,gcc编译错误表
2013-10-01 18:54 3079gcc bug : ##’ cannot appear at ... -
负数转化为整数
2013-10-01 12:02 1390负数转化为整数 int a = -1321313; 12 ... -
STDIN_FILENO的作用及与stdin 的区别
2013-09-08 14:48 917if(NULL == fgets(msg,100,stdi ... -
c++ 动态内存分配
2013-08-28 22:35 871先看一段代码: [cpp] view plaincopy ... -
c 专家编程
2013-08-13 17:06 732总结: -2> int * a = NUL ... -
c语言api
2013-07-31 21:06 700原型:extern int isalnum(int c); 用 ... -
c 语言无符号类型使用注意,类型升级
2013-07-30 14:37 637#define SS sizeof(int) 5 int ... -
判断两个一个链表是否存在循环(C专家编程中的问题)
2013-06-24 15:35 933判断两个一个链表是否存在循环(C专家编程中的问题) #incl ... -
atoi源码
2013-05-14 19:32 1300原文: http://blog.csdn.net/eroswa ... -
c语言特殊字符串复制
2013-05-06 01:59 8842.strcpy和memcpy主要有以下3方面的区别。 2.1 ... -
《APUE》:线程和fork(父子进程锁)
2013-04-29 21:07 1211《Unix环境高级编程》这本书附带了许多短小精美的小程序,我在 ... -
Linux多线程同步的几种方式
2013-04-22 22:49 811Linux多线程同步的几种方式 线程的最大特点是资 ... -
sphinx 安装
2013-04-10 19:54 550[@zw-76-80 soft]$ rpm -ivh MySQ ... -
防止pause和alrm产生竞争
2013-04-08 22:51 8701 #include<stdio.h> 2 ... -
关于linux环境下信号SIGCHLD的排队机制
2013-04-07 22:13 1585下面是关于在SIGCHLD的一遍网上的摘要,但是在linux中 ... -
alarm与pause
2013-04-07 20:12 7921 #include<stdio.h> 2 ... -
Linux下的定时器
2013-04-07 20:05 658Linux下的定时器有两种,以下分别介绍: 1、 ... -
linux 信号量
2013-04-04 22:24 718目录 SIGCHLD 描述 编辑本段SIG ...
相关推荐
《永磁无刷直流电机控制系统与软件综合研究——集成电机计算软件、电机控制器及电磁设计软件的创新设计与实践》,永磁无刷直流电机计算与控制软件:高效电机控制器与电磁设计工具,永磁无刷直流电机计算软件,电机控制器,无刷电机设计软件,电机电磁设计软件 ,永磁无刷直流电机计算软件; 电机控制器; 无刷电机设计软件; 电机电磁设计软件,无刷电机设计专家:永磁无刷直流电机计算与控制器设计软件
新能源汽车VCU开发模型及策略详解:从控制策略到软件设计全面解析,新能源汽车VCU开发模型及策略详解:从控制策略到软件设计全面解析,新能源汽车VCU开发模型及控制策略,MBD电控开发 新能源汽车大势所向,紧缺VCU电控开发工程师,特别是涉及新能源三电系统,工资仅仅低于无人驾驶、智能驾驶岗位。 ——含控制策略模型 整车控制策略详细文档 通讯协议文档 接口定义 软件设计说明文档 等(超详细,看懂VCU电控策略开发就通了) 内容如下: 新能源汽车整车控制器VCU学习模型,适用于初学者。 1、模型包含高压上下电,行驶模式管理,能量回馈,充电模式管理,附件管理,远程控制,诊断辅助功能。 2、软件说明书(控制策略说明书) 3、模型有部分中文注释 对想着手或刚开始学习整车控制器自动代码生成或刚接触整车控制器有很大帮助。 ,新能源汽车VCU开发模型; 控制策略; MBD电控开发; 模型学习; 代码生成; 整车控制器; 能量回馈; 诊断辅助功能,新能源汽车电控开发详解:VCU控制策略模型及学习手册
内容概要:本文详细介绍了两种利用 Python 读取 Excel 文件的不同方法,分别是基于 pandas 和 openpyxl。对于想要利用Python 处理 Excel 数据的读者来说,文中不仅提供了简洁明了的具体代码片段以及执行效果展示,还针对每个库的应用特性进行了深度解析。此外,文档提到了一些进阶应用技巧如只读特定的工作薄、过滤某些列等,同时强调了需要注意的地方(像是路径设置、engine 参数调整之类),让读者可以在面对实际项目需求时做出更加明智的选择和技术选型。 适合人群:对 Python 有基本掌握并希望提升数据读取能力的开发人员。 使用场景及目标:适用于任何涉及到批量数据导入或是与 Excel 进行交互的业务流程。无论是做初步的数据探索还是深入挖掘隐藏于电子表格背后的故事,亦或是仅为了简化日常办公自动化任务都可以从中受益。最终目标帮助使用者熟悉两大主流 Excel 解决方案的技术特性和最佳实践。 阅读建议:本文既是一份详尽的学习指南也是一份方便随时查阅的手册。因此初学者应当认真研究所提供的示例,而有一定经验者也可以快速定位到感兴趣的部分查看关键要点。
# 医护人员排班系统 ## 1. 项目介绍 本系统是一个基于SpringBoot框架开发的医护人员排班管理系统,用于医院管理医护人员的排班、调班等工作。系统提供了完整的排班管理功能,包括科室管理、人员管理、排班规则配置、自动排班等功能。 ## 2. 系统功能模块 ### 2.1 基础信息管理 - 科室信息管理:维护医院各科室基本信息 - 医护人员管理:管理医生、护士等医护人员信息 - 排班类型管理:配置不同的排班类型(如:早班、中班、晚班等) ### 2.2 排班管理 - 排班规则配置:设置各科室排班规则 - 自动排班:根据规则自动生成排班计划 - 排班调整:手动调整排班计划 - 排班查询:查看各科室排班情况 ### 2.3 系统管理 - 用户管理:管理系统用户 - 角色权限:配置不同角色的操作权限 - 系统设置:管理系统基础配置 ## 3. 技术架构 ### 3.1 开发环境 - JDK 1.8 - Maven 3.6 - MySQL 5.7 - SpringBoot 2.2.2 ### 3.2 技术栈 - 后端框架:SpringBoot - 持久层:MyBatis-Plus - 数据库:MySQL - 前端框架:Vue.js - 权限管理:Spring Security ## 4. 数据库设计 主要数据表: - 科室信息表(keshixinxi) - 医护人员表(yihurengyuan) - 排班类型表(paibanleixing) - 排班信息表(paibanxinxi) - 用户表(user) ## 5. 部署说明 ### 5.1 环境要求 - JDK 1.8+ - MySQL 5.7+ - Maven 3.6+ ### 5.2 部署步骤 1. 创建数据库并导入SQL脚本 2. 修改application.yml中的数据库配置 3. 执行maven打包命令:mvn clean package 4. 运行jar包:java -jar xxx.jar ## 6. 使用说明 ### 6.1 系统登录 - 管理员账号:admin - 初始密码:admin ### 6.2 基本操作流程 1. 维护基础信息(科室、人员等) 2. 配置排班规则 3. 生成排班计划 4. 查看和调整排班 ## 7. 注意事项 1. 首次使用请及时修改管理员密码 2. 定期备份数据库 3. 建议定期检查和优化排班规则
MATLAB仿真的夫琅禾费衍射强度图:圆孔、圆环、矩形孔定制研究,MATLAB仿真:夫琅禾费衍射强度图的可定制性——以圆孔、圆环及矩形孔为例的研究分析,MATLAB夫琅禾费衍射强度图仿真 圆孔,圆环,矩形孔可定制。 ,MATLAB; 夫琅禾费衍射; 强度图仿真; 圆孔; 圆环; 矩形孔; 可定制。,MATLAB仿真夫琅禾费衍射强度图:定制孔型(圆孔/圆环/矩形)
详细介绍及样例数据:https://blog.csdn.net/samLi0620/article/details/145652300
基于Dugoff轮胎模型与B08_01基础建模的七自由度车辆动力学模型验证:利用MATLAB 2018及以上版本与CarSim 2020.0软件的仿真对比研究,基于Dugoff轮胎模型与B08_01框架的七自由度车辆动力学模型验证——使用MATLAB 2018及以上版本与CarSim 2020.0软件进行仿真对比研究,七自由度车辆动力学模型验证(Dugoff轮胎模型,B08_01基础上建模) 1.软件: MATLAB 2018以上;CarSim 2020.0 2.介绍: 基于Dugoff轮胎模型和车身动力学公式,搭建7DOF车辆动力学Simulink模型,对相关变量(质心侧偏角,横摆角速度,纵、横向速度及加速度)进行CarSim对比验证。 ,核心关键词:七自由度车辆动力学模型验证; Dugoff轮胎模型; B08_01建模基础; MATLAB 2018以上; CarSim 2020.0; Simulink模型; 变量对比验证。,基于Dugoff轮胎模型的七自由度车辆动力学模型验证与CarSim对比
【毕业设计】基于Java+servlet+jsp+css+js+mysql实现“转赚”二手交易平台_pgj
微猫恋爱聊妹术小程序源码介绍: 微猫恋爱聊妹术小程序源码是一款全新升级的聊天工具,它采用全新主题和UI,完美支持分享朋友圈功能。同时,它的独立后台也进行了大规模更新,让操作更加简单。其中,课堂页面、搜索页面和子话术列表页面等,均增加了流量主展示,具有超多的功能。 安装教程: 您可以先加入微猫恋爱聊妹术小程序源码的赞助群,然后在群内找到魔方安装说明。根据源码编号找到相应的安装说明,非常详细,让您轻松完成安装。
电气安装工程安全技术规程_蒋凯,杨华甫,马仲范,王清禄译;孙照森校;鞍钢工程技术编委会编
基于Copula函数的风光空间相关性联合场景生成与K-means聚类削减MATLAB研究,基于Copula函数的风光空间相关性联合场景生成与K-means聚类削减算法研究,基于copula的风光联合场景生成?K-means聚类并削减 MATLAB 由于目前大多数研究的是不计风光出力之间的相关性影响,但是地理位置相近的风电机组和光伏机组具有极大的相关性。 因此,采用 Copula 函数作为风电、光伏联合概率分布,生成风、光考虑空间相关性联合出力场景,在此基础上,基于Kmeans算法,分别对风光场景进行聚类,从而实现大规模场景的削减,削减到5个场景,最后得出每个场景的概率与每个对应场景相乘求和得到不确定性出力 ,基于Copula的风光联合场景生成; K-means聚类削减; 空间相关性; 概率分布; 场景削减,基于Copula与K-means的风光联合场景生成与削减研究
模块化多电平变流器MMC的VSG控制技术研究:基于MATLAB-Simulink的仿真分析与定制实现——支持三相与任意电平数,构网型模块化多电平变流器MMC的VSG控制策略与仿真模型:三相负荷变动下的虚拟同步发电机控制研究,构网型 模块化多电平变流器 MMC 的VSG控制 同步发电机控制 MATLAB–Simulink仿真模型,可按需求定制 10电平.14电平,任意电平可做。 三相MMC,采用VSG控制。 设置负荷变动,调整有功无功,保持电网电压和频率 ,构网型模块化多电平变流器; MMC的VSG控制; 虚拟同步发电机控制; MATLAB–Simulink仿真模型; 任意电平可做; 三相MMC; 负荷变动; 有功无功调整; 电网电压和频率保持。,基于VSG控制的模块化多电平变流器(MMC)的构网型仿真模型
暗通道算法DCP-Python实现
南师大实验室安全准入知识供学习
纯openMV寻迹小车.zip
【毕业设计】基于Java mvc架构开发的完整购物网站
以下是针对初学者的 **51单片机入门教程**,内容涵盖基础概念、开发环境搭建、编程实践及常见应用示例,帮助你快速上手。
springboot医院信管系统--
springboot私人健身与教练预约管理系统--
yolov8-0的资源