EOF是不是字符
2020-07-20
编译:老齐
与本文相关的图书推荐:《Python大学实用教程》
本书是面向零基础学习者的Python入门读物,包含完整的Python语法知识、针对性的练习题,本书强调学习中的实战,案例和习题均从开发实践的角度进行设计。
什么是 EOF
?
百度百科上这样解释:EOF是一个计算机术语,为End Of File的缩写,在操作系统中表示资料源无更多的资料可读取。资料源通常称为档案或串流。通常在文本的最后存在此字符表示资料结束。
在这个解释中,认为EOF
是表示文件结束的字符——这就是本文要重点讨论的,EOF是不是一个字符?
在Unix、Linux系统上,用C语言读写文件,经常会遇到EOF。之所以很多人认为EOF是一个字符串,可能是因为在C语言的程序中,会用getchar()
和getc()
检查是否遇到了EOF。
1 |
|
或者:
1 | FILE *fp; |
这样,getchar()
或getc()
都从输入中获取下一个字符。 因此,这可能导致我们对EOF的本质感到困惑。当然,这仅仅是一种猜测。下面看看另外的理由。
什么是字符?字符可以看成是文本的最小组成党委,比如A, b, B
等都是字符。在Unicode字符集中,每个字符都对应一个数字编码,例如大写字母A
的字符编码是65(用十进制表示)。在Python 3中,可以这样查看:
1 | 'A') ord( |
或者,也可以在Unix/Linux中这样查看:
1 | man ascii |
下面用一下段C语言程序,来看看EOF。在ANSI C中,EOF在<stdio.h>
标准库中,它的数字编码值一般是-1
。将下面的程序保存为printeof.c
,并运行:
1 |
|
1 | gcc -o printeof printeof.c |
在Mac OS和Ubuntu系统上测试,都是输出-1
。
那么,那个“字符”的数字编码是-1
呢?
那就用前面演示的Python中的函数,来检索一下,看看-1
对应的字符是什么。
1 | # 在Python交互模式中 |
没有!在ASCII字符集中没有任何一个字符的数字编码是-1
。
所以,现在可以断言:EOF不是一个字符。
再换一个角度考察。
如果EOF是字符,你就能在文件末尾“看”到它。下面检测一下文本文件helloworld.txt的内容,并且用xxd
指令输出这个文件的二进制/十六进制形式。
1 | cat helloworld.txt |
在以十六进制表示的输出内容中,此文件是以0a
结尾的,那么这个0a
是什么呢?
1 | # Python交互模式 |
事实再次说明,EOF不是字符。
它是什么?
EOF(end-of-file)是操作系统内核提供的一个条件,它可以被程序检测到。
下面我们来看一下,几种不同的编程语言在通过高级I/O接口读一个文本文件的时候,是如何检测到这条件的(用于检测的所有程序,可以从代码仓库获得:https://github.com/rspivak/2x25/tree/master/eofnotchar)
C语言程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/* 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;
}编译:
1
gcc -o mcat mcat.c
执行:
1
2$ ./mcat helloworld.txt
Hello world!- 此程序通过命令行参数打开一个文件
while
循环一次一个字节地将文件中的内容复制到标准输出,一直到文件末尾- 如果遇到EOF,则关闭文件,并返回客户端
Python 3 程序
1
2
3
4
5
6
7
8
9# mcat.py
import sys
with open(sys.argv[1]) as fin:
while True:
c = fin.read(1) # read max 1 char
if c == '': # EOF
break
print(c, end='')1
2python mcat.py helloworld.txt
Hello world!在Python3.8+中,还可以用海象运算符,精简程序:
1
2
3
4
5
6# mcat38.py
import sys
with open(sys.argv[1]) as fin:
while (c := fin.read(1)) != '': # read max 1 char at a time until EOF
print(c, end='')1
2python3.8 mcat38.py helloworld.txt
Hello world!Go 程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 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()
}1
2go run mcat.go helloworld.txt
Hello world!JavaScript(node.js)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/* 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.');
});1
2
3
4node mcat.js helloworld.txt
Hello world!
EOF: There will be no more data.上面的示例中的高级I/O例程如何确定文件结束条件?
在Linux系统上,例程直接或间接使用内核提供的read()
系统调用,例如,C语言中的getc()
使用read()
系统调用,当指示到end-of-file
条件,则返回EO。
read()`系统调用返回0代表EOF条件。
下面把前面的C语言程序改写一下,注意观察:
1 | /* syscat.c */ |
1 | gcc -o syscat syscat.c |
上面的代码中,注意观察read()
函数,返回的0就代表EOF,当然,Pytyon程序也可以改写。
1 | # syscat.py |
1 | python syscat.py helloworld.txt |
Python3.8+的程序
1 | # syscat38.py |
1 | 执行结果 |
至此,应该明确了一下几点:
- EOF不是Unicode字符集中的字符
- 在Unix/Linux系统中,文件的最后找不到所谓的EOF字符,根本就没有这样一个字符
- EOF是程序能够检测到的Unix/Linux内核提供的一个条件
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
关注微信公众号,读文章、听课程,提升技能