添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

于是,我就困惑了。

如果EOF是一个特殊字符,那么假定每个文本文件的结尾都有一个EOF(也就是-1),还是可以做到的,因为文本对应的ASCII码都是正值,不可能有负值。但是,二进制文件怎么办呢?怎么处理文件内部包含的-1呢?

这个问题让我想了很久,后来查了资料才知道, 在Linux系统之中,EOF根本不是一个字符,而是当系统读取到文件结尾,所返回的一个信号值(也就是-1)。 至于系统怎么知道文件的结尾,资料上说是通过比较文件的长度。

所以,处理文件可以写成下面这样:

int c;

while ((c = fgetc(fp)) != EOF) {

do something

这样写有一个 问题 。fgetc()不仅是遇到文件结尾时返回EOF,而且当发生错误时,也会返回EOF。因此,C语言又提供了feof()函数,用来保证确实是到了文件结尾。上面的代码feof()版本的写法就是:

int c;

while (!feof(fp)) {

c = fgetc(fp);

do something;

但是,这样写也有 问题 。fgetc()读取文件的最后一个字符以后,C语言的feof()函数依然返回0,表明没有到达文件结尾;只有当fgetc()向后再读取一个字符(即越过最后一个字符),feof()才会返回一个非零值,表示到达文件结尾。

所以,按照上面这样写法,如果一个文件含有n个字符,那么while循环的内部操作会运行n+1次。所以,最保险的 写法 是像下面这样:

int c = fgetc(fp);

while (c != EOF) {

do something;
c = fgetc(fp);

if (feof(fp)) {

printf("\n End of file reached.");

} else {

printf("\n Something went wrong.");

除了表示文件结尾,EOF还可以表示标准输入的结尾。

int c;

while ((c = getchar()) != EOF) {

putchar(c);

但是,标准输入与文件不一样,无法事先知道输入的长度,必须手动输入一个字符,表示到达EOF。

Linux中,在新的一行的开头,按下Ctrl-D,就代表EOF(如果在一行的中间按下Ctrl-D,则表示输出"标准输入"的缓存区,所以这时必须按两次Ctrl-D);Windows中,Ctrl-Z表示EOF。(顺便提一句,Linux中按下Ctrl-Z,表示将该进程中断,在后台挂起,用fg命令可以重新切回到前台;按下Ctrl-C表示终止该进程。)

那么,如果真的想输入Ctrl-D怎么办?这时必须先按下Ctrl-V,然后就可以输入Ctrl-D,系统就不会认为这是EOF信号。 Ctrl-V 表示按"字面含义"解读下一个输入,要是想按"字面含义"输入Ctrl-V,连续输入两次就行了。

这里的思路是对的,但说法上有问题。

在阮一峰先生的那篇推荐C语言教材的文章中,我说过,阮先生该看一些基础性的文档。C 语言中,函数的类型是由函数声明确定的,参数和返回值的类型都不能变。您看 K&R 有困惑,说明您和其它学习者交流不足,有些课堂上接触的基础概念您不熟悉。
C 语言标准库中,处理单个字符的函数一般都是将字符当作 int 型处理的,所以不存在什么类型转换,直接就可以将 EOF 和 char 返回为不冲突的二进制表达形式。

应该是这样吧:
首先肯定的是返回值都是int型的。
如果读二进制读到-1,则其实读到的是0xff,fgetc返回时将其转为int型即0x00ff,对于int型来说,0x00ff不等于-1;
如果fgetc返回值是-1,则说明返回的是int型的-1即0xffff;

俺以前一直都是不求甚解,看到大伙讨论,man了一下fgetc:

fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on end of file or error.

确实是无符号字符的方式;

另外,fgetc只是取流的内容的接口,与流的实际存在形式是分离的,因此设定一些实现无关的标识来标明特殊情况也是合理的。

fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on end of file or error.

这两点肯定是对的!

另外在K&R书中,提到一个经典的bug:

char c;
while((c = fgetc(stdin)) != EOF)
putchar(c);
......

fgetc(stdin)返回的一个unsigned char然后转换成int,然后在赋值运算:
c = fgetc(stdin)中又转换为char。最后在“!=”运算符中又要从char转换到int。
那么:(int)0xff ?= (EOF = 0xffff)呢?
如果系统的char是默认是signed char,那么没有问题。因为(int)((signed char)0xff) == (EOF = 0xffff),可以正确的表示到达了文件(输入)末尾的概念。
但如果系统的char是默认是unsigned char,那么就有问题了。因为(int)((unsigned char)0xff != (EOF = 0xffff)。这样就没有办法正确的表达文件(输入)到达末尾的概念。这样就成了一个死循环。

printf("i = %d = 0x%x\n", i, i);
printf("j = %d = 0x%x\n", j, j);
printf("sc == eof: %d\n", (sc == eof));
printf("uc == eof: %d\n", (uc == eof));

return 0;
运行结果:

digdeep@ubuntu:~/uulp$ ./char
i = -1 = 0xffffffff
j = 255 = 0xff
sc == eof: 1
uc == eof: 0

引用wdd的发言:
在Linux系统之中,EOF根本不是一个字符,而是当系统读取到文件结尾,所返回的一个信号值(也就是-1) int c;  while ((c = fgetc(fp)) != EOF)------------有个问题请教一下,如果c = fgetc(fp) 读到的是二进制的-1,怎么区分读到的c(-1)跟信号值(-1)呢?

-1 是1111 1111 文件里不出现这个就行了。

阮先生,谢谢你的分享,你的这篇博文让我回想到以前学习的一篇文章(http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1048865140&id=1043284351),里面有关于EOF的解释。
我根据自己的理解、用工具观察的二进制结果加上程序代码,做拙文一篇, 希望能够回答上面的问题。
http://www.cnblogs.com/cando/archive/2012/09/17/2689070.html

只要仔细看这个函数的说明就不会有这个问题了。

fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on end of file or error。

"unsigned char cast to an int",unsigned char 转换成的 int 是不会出现 负数 的,所以 EOF 可以和文件里面的字符区别开来。