前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >EOF是不是字符

EOF是不是字符

作者头像
老齐
发布2020-07-23 14:38:21
4.5K0
发布2020-07-23 14:38:21
举报
文章被收录于专栏:老齐教室

什么是 EOF

百度百科上这样解释:EOF是一个计算机术语,为End Of File的缩写,在操作系统中表示资料源无更多的资料可读取。资料源通常称为档案或串流。通常在文本的最后存在此字符表示资料结束。

在这个解释中,认为EOF是表示文件结束的字符——这就是本文要重点讨论的,EOF是不是一个字符?

在Unix、Linux系统上,用C语言读写文件,经常会遇到EOF。之所以很多人认为EOF是一个字符串,可能是因为在C语言的程序中,会用getchar()getc()检查是否遇到了EOF。

代码语言:javascript
复制
#include <stdio.h>
...
while ((c = getchar()) != EOF)
   putchar(c);

或者:

代码语言:javascript
复制
FILE *fp;
int c;
...
while ((c = getc(fp)) != EOF)
  putc(c, stdout);

这样,getchar()getc()都从输入中获取下一个字符。因此,这可能导致我们对EOF的本质感到困惑。当然,这仅仅是一种猜测。下面看看另外的理由。

什么是字符?字符可以看成是文本的最小组成党委,比如A, b, B等都是字符。在Unicode字符集中,每个字符都对应一个数字编码,例如大写字母A的字符编码是65(用十进制表示)。在Python 3中,可以这样查看:

代码语言:javascript
复制
>>> ord('A')
65
>>> chr(65)
'A'

或者,也可以在Unix/Linux中这样查看:

代码语言:javascript
复制
$ man ascii

下面用一下段C语言程序,来看看EOF。在ANSI C中,EOF在<stdio.h>标准库中,它的数字编码值一般是-1。将下面的程序保存为printeof.c,并运行:

代码语言:javascript
复制
#include <stdio.h>

int main(int argc, char *argv[])
{
  printf("EOF value on my system: %d\n", EOF);
  return 0;
}
代码语言:javascript
复制
$ gcc -o printeof printeof.c

$ ./printeof
EOF value on my system: -1

在Mac OS和Ubuntu系统上测试,都是输出-1

那么,那个“字符”的数字编码是-1呢?

那就用前面演示的Python中的函数,来检索一下,看看-1对应的字符是什么。

代码语言:javascript
复制
# 在Python交互模式中
>>> chr(-1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: chr() arg not in range(0x110000)

没有!在ASCII字符集中没有任何一个字符的数字编码是-1

所以,现在可以断言:EOF不是一个字符

再换一个角度考察。

如果EOF是字符,你就能在文件末尾“看”到它。下面检测一下文本文件helloworld.txt的内容,并且用xxd指令输出这个文件的二进制/十六进制形式。

代码语言:javascript
复制
$ cat helloworld.txt
Hello world!

$ xxd helloworld.txt
00000000: 4865 6c6c 6f20 776f 726c 6421 0a         Hello world!.

在以十六进制表示的输出内容中,此文件是以0a结尾的,那么这个0a是什么呢?

代码语言:javascript
复制
# Python交互模式
>>> chr(0x0a)
'\n'

事实再次说明,EOF不是字符。

它是什么?

EOF(end-of-file)是操作系统内核提供的一个条件,它可以被程序检测到。

下面我们来看一下,几种不同的编程语言在通过高级I/O接口读一个文本文件的时候,是如何检测到这条件的(用于检测的所有程序,可以从代码仓库获得:https://github.com/rspivak/2x25/tree/master/eofnotchar)

  1. C语言程序/* mcat.c */#include <stdio.h>int main(int argc, char *argv[]){ FILE *fp; int c; if ((fp = fopen(*++argv, "r")) == NULL) { printf("mcat: can't open %s\n", *argv); return 1; } while ((c = getc(fp)) != EOF) putc(c, stdout); fclose(fp); return 0;}编译:
    • 此程序通过命令行参数打开一个文件
    • while循环一次一个字节地将文件中的内容复制到标准输出,一直到文件末尾
    • 如果遇到EOF,则关闭文件,并返回客户端
  2. Python 3 程序# mcat.pyimport syswith open(sys.argv[1]) as fin: while True: c = fin.read(1) # read max 1 char if c == '': # EOF break print(c, end='') python mcat.py helloworld.txtHello world!在Python3.8+中,还可以用海象运算符,精简程序:# mcat38.pyimport syswith open(sys.argv[1]) as fin: while (c := fin.read(1)) != '': # read max 1 char at a time until EOF print(c, end='') python3.8 mcat38.py helloworld.txtHello world!
  3. Go 程序 // mcat.go package main import ( "fmt" "os" "io" ) func main() { file, err := os.Open(os.Args[1]) if err != nil { fmt.Fprintf(os.Stderr, "mcat: %v\n", err) os.Exit(1) } buffer := make([]byte, 1) // 1-byte buffer for { bytesread, err := file.Read(buffer) if err == io.EOF { break } fmt.Print(string(buffer[:bytesread])) } file.Close() } $ go run mcat.go helloworld.txt Hello world!
  4. JavaScript(node.js) /* mcat.js */ const fs = require('fs'); const process = require('process'); const fileName = process.argv[2]; var readable = fs.createReadStream(fileName, { encoding: 'utf8', fd: null, }); readable.on('readable', function() { var chunk; while ((chunk = readable.read(1)) !== null) { process.stdout.write(chunk); /* chunk is one byte */ } }); readable.on('end', () => { console.log('\nEOF: There will be no more data.'); }); $ node mcat.js helloworld.txt Hello world! EOF: There will be no more data. 上面的示例中的高级I/O例程如何确定文件结束条件?

在Linux系统上,例程直接或间接使用内核提供的read()系统调用,例如,C语言中的getc()使用read()系统调用,当指示到end-of-file条件,则返回EOread()`系统调用返回0代表EOF条件。

下面把前面的C语言程序改写一下,注意观察:

代码语言:javascript
复制
/* syscat.c */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
  int fd;
  char c;

  fd = open(argv[1], O_RDONLY, 0);

  while (read(fd, &c, 1) != 0)
    write(STDOUT_FILENO, &c, 1);

  return 0;
}
代码语言:javascript
复制
$ gcc -o syscat syscat.c

$ ./syscat helloworld.txt
Hello world!

上面的代码中,注意观察read()函数,返回的0就代表EOF,当然,Pytyon程序也可以改写。

代码语言:javascript
复制
# syscat.py
import sys
import os

fd = os.open(sys.argv[1], os.O_RDONLY)

while True:
    c = os.read(fd, 1)
    if not c:  # EOF
        break
    os.write(sys.stdout.fileno(), c)
代码语言:javascript
复制
$ python syscat.py helloworld.txt
Hello world!

Python3.8+的程序

代码语言:javascript
复制
# syscat38.py
import sys
import os

fd = os.open(sys.argv[1], os.O_RDONLY)

while c := os.read(fd, 1):
    os.write(sys.stdout.fileno(), c)
代码语言:javascript
复制
# 执行结果
$ python3.8 syscat38.py helloworld.txt
Hello world!

至此,应该明确了一下几点:

  • EOF不是Unicode字符集中的字符
  • 在Unix/Linux系统中,文件的最后找不到所谓的EOF字符,根本就没有这样一个字符
  • EOF是程序能够检测到的Unix/Linux内核提供的一个条件

参考资料:https://ruslanspivak.com/eofnotchar/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老齐教室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档