• EOF是什么?(转)


    我学习C语言的时候,遇到的一个问题就是EOF

    它是end of file的缩写,表示"文字流"(stream)的结尾。这里的"文字流",可以是文件(file),也可以是标准输入(stdin)。

    比如,下面这段代码就表示,如果不是文件结尾,就把文件的内容复制到屏幕上。

      int c;

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

        putchar (c);

      }

    很自然地,我就以为,每个文件的结尾处,有一个叫做EOF的特殊字符,读取到这个字符,操作系统就认为文件结束了。

    但是,后来我发现,EOF不是特殊字符,而是一个定义在头文件stdio.h的常量,一般等于-1。

      #define EOF (-1)

    于是,我就困惑了。

    如果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,连续输入两次就行了。

     

    留言(22条)

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

    @wdd:

    我的理解是这样的,不一定对,欢迎讨论。

    根据函数定义,除了EOF,fgetc()函数返回的都是unsigned char类型,而c是int类型,也就是说,这里发生了类型转换。除了最后一个字节,c的前几个字节肯定都是0。所以,除非出现EOF,否则c肯定是一个正值,不可能等于-1。

    你输入-1实际上是‘-’ ‘1’。除非你真用二进制读写函数读写文件那就不存在EOF而是按字节块读写,fgetc是文本型处理文件的,不存在读2进制的-1,一次只处理一个字节,这里有个重要概念,文件分为:文本视图,2进制视图。打开文件分文本模式打开,2进制打开。处理也分文本模式和2进制模式。所以不要混淆

    补充一点,Linux和Windows的输入分别是采用非阻塞式和阻塞式,也就是说Linux中你按下Ctrl+D后,程序立即响应,将键盘输入的东西放到输入缓冲区中(同时忽略输入的这个Ctrl+D),而Windows下则是你按了回车以后它才去看你输入有没有Ctrl+Z的

    对了,至于那个-1,请务必好好研读下ASC II,他是7位0~127个字符,第8位是没有用的,所以signed char中如果第8位为1,表示负数也好正数也好,在ASC表里都不对应任何字符

    int
    fgetc(FILE *fp)
    {
    return (__sgetc(fp));
    }
    google codesearch搜到的代码是这样的,返回值是int,不是unsigned int

    引用阮一峰的发言:

    根据函数定义,除了EOF,fgetc()函数返回的都是unsigned char类型,而c是int类型,也就是说,这里发生了类型转换。除了最后一个字节,c的前几个字节肯定都是0。所以,除非出现EOF,否则c肯定是一个正值,不可能等于-1。

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

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

    引用iSayme的发言:

    google codesearch搜到的代码是这样的,返回值是int,不是unsigned int

    fgetc要返回EOF,返回值的类型肯定是int啊。

    我是说,除了EOF,其他返回值都是unsigned char。

    引用wdd的发言:

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

    可以区分的,原因:32位系统下,int是4字节的,-1对应的是0xFFFFFFFF,而fgetc()是一个个字节地读,所以,如果有一个二进制文件的4个字节是-1,那么实际上会读出4个0xFF来。

    引用iSayme的发言:

    int
    fgetc(FILE *fp)
    {
    return (__sgetc(fp));
    }
    google codesearch搜到的代码是这样的,返回值是int,不是unsigned int

    这个要看编译器。。。 每个编译器规范不一样,ANSI C不规定是signed 还是 unsigned ,char本身就是用整形实现的

    不错,既懂程序又懂英语,人才。

    引用Dan的发言:


    这个要看编译器。。。 每个编译器规范不一样,ANSI C不规定是signed 还是 unsigned ,char本身就是用整形实现的

    其实 C99 里的7.19.7.1小节(n1256.pdf 的第308页)规定了 fgetc 要按照 unsigned char 来读文件中的字节,并转为 int 型返回。

    虽然是一个简单的问题,但看你娓娓道来十分有趣。

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

    阮兄,您的rss在谷歌reader上总是报错啊

    受教了!! 謝謝你!! ^^b

    俺以前一直都是不求甚解,看到大伙讨论,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只是取流的内容的接口,与流的实际存在形式是分离的,因此设定一些实现无关的标识来标明特殊情况也是合理的。

    谢谢分享。一直都不太懂eof,现在廓然开朗。

    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)。这样就没有办法正确的表达文件(输入)到达末尾的概念。这样就成了一个死循环。

    给出一段严重代码:

    给出一段验证代码:
    #include 
    int main()
    {
    signed char sc = 0xff;
    unsigned char uc = 0xff;
    int i = sc;
    int j = uc;
    int eof = -1; // END OF FILE

    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

    EOF 叫 End of File Condition,自然不是字符
    还有好多神马:

    tty: Ctrl+D
    cmd: Ctrl+Z
    std::basic_ios::eof()
    std::istream_iterator(is)
    ...

    我记得Windows CMD中得按下^z后再按Enter才生效,而且是不区分在空行首与否的,即不像Unix的行首^D那样直接flush buffer…

     
     
    注:若要参与讨论可以到原作者blog参与,知识在交流中积累
  • 相关阅读:
    297.白盒测试
    301.多媒体讲台使用事项
    289.南信大知网登录
    296.deepin下载安装、root改密、cpu不降频、修改快捷键、创建启动器快捷方式、win文件访问、直接进win无deepin启动项
    295.博客园win&苹果PC客户端开源项目整理
    利用loganalyzer展示MySQL中rsyslog日志
    利用inotify和rsync实现数据的实时同步
    samba服务配置实践
    NFS服务配置实践
    FTP服务配置实践
  • 原文地址:https://www.cnblogs.com/bizhu/p/2532602.html
Copyright © 2020-2023  润新知