转载自:http://woshijpf.github.io/2016/07/07/Linux%E4%B8%AD%E7%9A%84EOF%E5%88%B0%E5%BA%95%E6%98%AF%E4%BB%80%E4%B9%88/

EOF 的定义

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

以前在学习 C 语言文件操作的时候,一直记得 EOF 就是一个标记,通过它可以判断程序是否读取到文件的末尾了,例如下面的这段代码就是将一个文本文件中的字符输出到标准输出中,并通过 EOF 来判断程序是否读到了文件末尾:

#include <stdio.h>#define FILENAME "gdb_test.c"int main()
{FILE* fd;int c;fd = fopen(FILENAME, "r");while ((c = fgetc(fd)) != EOF){fputc(c, stdout);}return 0;
}

所以,我一直很好奇 EOF 到底是什么个什么东西,它只是一个简单的常量还是一个特殊的字符呢?

EOF 定义在 /usr/include/stdio.h 文件中:

/* End of file character.
Some things throughout the library rely on this being -1.  */#ifndef EOF# define EOF (-1)#endif

从上面 EOF 的定义我们可以看出 EOF 本质上就是一个值为-1的常量!

怎么通过 EOF 来判断程序是否读取到了文件末尾?

Linux 系统一个非常重要的思想就是:一切皆文件。不管是标准输入,文件系统中的普通文本文件,还是网络流都可以看做是文件,都可以通过 read/write 函数进行读写操作。因此,不同的文件类型,判断是否读取到文件末尾的方式也就有所不同,下面就按照

  • 普通文本文件
  • 标准输入文件(stdin)
  • socket 流文件

这三类文件来介绍判断它们是否读取到文件末尾的方法。

普通文本文件

这里的普通文件指的是我们平时在通过文件管理器所能看到那些文本文件,它存在于 Linux 中的文件系统中,并且文件的大小是固定的。

对于这种文件,Linux 系统判断普通文本文件是否读取到文件末尾的方法是:read 函数会对所打开的文件维护一个读取指针,然后根据这个指针跟文件开始位置的指针值相减得到一个相对于文件开始位置的偏移字节数,最后通过这样一个偏移字节数和文件本身的大小进行一个比较,如果相对于文件开始位置的偏移字节数大于文件本身的大小,那么就返回一个 EOF 常量,说明此时已经读取到文件末尾了。

所以,按照上面这样写法,如果一个文件含有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.");
}

标准输入文件

标准输入文件(stdin)它对应的是外设键盘输入,而在 Linux 系统中它被抽象成一个文件,准确地说是一个流文件。这种文件和上面普通文本文件最大的区别就是它的文件大小是不固定的,它就像是一个水管的进水端,可以在任何时候都可以接收输入。

正是因为标准输入文件这种流式的特点,决定了无法通过前面提到那种比较文件大小方法来判断是否读取到了文件末尾。因此,Linux 系统判断标准输入文件是否读取到文件末尾的方法是:设置一个特殊的输入标记来表示文件末尾,而在Linux 系统中这个标记就是组合键Ctrl+D,当系统捕获到这个组合键时,就让 r read 函数返回一个 EOF 常量,告知程序已经读取到标准文件的末尾了。

socket 流文件

socket 流文件和标准输入文件类似都是流式文件,并且它是从网络上进行数据读取,所以上面两种判断文件是否读取到末尾的方法都不适用于 socket 流文件。

那么客户端进程怎么判断服务端进程是否已经写完所有数据?
在 socket 流文件中,当客户端进程通过 read 函数读取远程服务端进程发送过来的数据时,使用的是阻塞I/O的方式进行读取的。只要客户端和服务端之间的连接没有断开,如果服务端没有向 socket 写入数据,那么客户端的读操作就会阻塞,直到服务端中写入了新的数据。

如果服务端进程关闭了socket连接,那么客户端会接收到服务端发送过来的一个 TCP 协议的 FIN 数据包,然后客户端进程中原本阻塞着等待接收服务端进程数据的 read函数此时就会被唤醒,返回一个值 0。这跟我们前面提到两种文件读到文件末尾返回 EOF(值为-1)的情况有点差别,所以在程序中从 socket 进行读取操作时,判断数据流结束的标志不是 -1 而是 0。

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

所以,一个简单的从 socket 文件读取数据的样例代码,通常是下面这样的:

char recvline[MAX_LINE_LENGTH];
int read_count;
while ((read_count = read(sock_fd, recvline, MAX_LINE_LENGTH)) > 0)
{printf("%s\n", "String received from server: ");fputs(recvline, stdout);
}

总结

所以,一定要记住这样一个概念:EOF 是一个常量而不是一个字符!

Linux 中的 EOF 到底是什么?相关推荐

  1. linux socket eof,Linux 中的 EOF 到底是什么?

    EOF 的定义 EOF 是 end of file 的缩写,表示"文字流"(stream)的结尾.这里的"文字流",可以是文件(file),也可以是标准输入(s ...

  2. Linux中的shell到底是啥,它和bash的关系是什么?

    转载:这篇小文章用来扫清一个小小的盲区,首先建立一个概念Linux中的shell到底是什么? - 一步一个小脚印 - 博客园 (cnblogs.com) [一] shell的含义:首先shell的英文 ...

  3. Linux中的shell到底是什么?

    一.shell的含义: 首先shell的英文含义是"壳": 它是相对于内核来说的,因为它是建立在内核的基础上,面向于用户的一种表现形式,比如我们看到一个球,见到的是它的壳,而非核. ...

  4. Linux中的shell到底是什么

    (引自:https://zhidao.baidu.com/question/557066905.html) [一] shell的含义: 首先shell的英文含义是"壳": 它是相对 ...

  5. 一文彻底明白linux中的selinux到底是什么

    一.前言 安全增强型 Linux(Security-Enhanced Linux)简称 SELinux,它是一个 Linux 内核模块,也是 Linux 的一个安全子系统. SELinux 主要由美国 ...

  6. linux中的selinux到底是什么,本篇文章彻底明白

    原文链接https://www.phpyuan.com/235739.html,本文加以修改,若有侵权,请联系删除 一.前言 安全增强型 Linux(Security-Enhanced Linux)简 ...

  7. linux中EOF的用法梳理

    2019独角兽企业重金招聘Python工程师标准>>> linux中EOF的用法梳理 在linux运维工作中,我们会碰到这样一个场景: 执行脚本的时候,需要往一个文件里自动输入N行内 ...

  8. Linux与Windows关于标准输入中涉及EOF的处理方式

    在window下,在行尾加Ctrl+Z并且enter后并不会结束程序的运行,而只会将Ctrl+Z当成一个字符来解释,并且将在输入缓冲区中存储的本行数据输出. 要结束输入必须在新的一行(输入enter迫 ...

  9. Linux中main和初启函数,main 中的 argv和argc 到底是个啥意思?

    原标题:main 中的 argv和argc 到底是个啥意思? 前言 一般我们平时写main函数的话,一般都是写不带参数的比较多,而且也习惯了这样写:其实标准的形式写法,main函数是带两个参数的,这两 ...

最新文章

  1. 后处理程序文件大小的变量_【每日一题】(17题)面试官问:JS中事件流,事件处理程序,事件对象的理解?...
  2. 深入浅出:对MySQL主从配置的一些总结
  3. 【IOI2018】狼人【Kruscal重构树】【主席树】
  4. 【Java从0到架构师】Linux 应用 - 软件包管理、软件安装
  5. vc+ mfc 方法怎么被调用_Spring源码阅读(二)我的方法是怎么被自动调用的
  6. 结对编程_队友代码分析
  7. code1068 乌龟棋
  8. C语言基础课第三次作业
  9. 华为海思芯片 10 年备胎史!
  10. java离线地图web
  11. 文献管理工具EndNote X8 破解版,Windows版
  12. 向死而生的微信视频号,逆风翻盘的2020
  13. 硬盘(IDE硬盘与SCSI硬盘)在Linux标识的命名规则
  14. TiDB-explain详解
  15. QQ群怎么快速封群,如何举报骗子QQ群可以使之封群?
  16. d435i 深度相机运行踩坑大合集
  17. cherry Tree的严重漏洞
  18. 定时任务与网页去重、代理的使用
  19. gearsec.exe
  20. 看图记设计模式【五】,创建模式系列:原型模式

热门文章

  1. 测试岗/测试开发岗面试真题及参考答案
  2. warmup_csaw_2016
  3. 泰语专业论文选题有什么建议吗?
  4. 模拟SPI驱动SD卡
  5. element组件树形控件el-tree点击展开节点,节点重影
  6. 为什么这些年都不快乐
  7. Java EE结构理解 与Dao模式 no7.
  8. cygwin 安装 ffplay
  9. 对Java字符类型的深入了解(转贴) .
  10. sql语句的各种模糊查询