一 官方文档
按照我个人处理问题的习惯,先拿出官方文档的定义,然后再解决问题,这样更准去,也更具有说服力。参考自Java SE 8:抽象类java.io.InputStream
public abstract int read() throws IOException
Reads the next byte of data from the input stream. The value byte is returned as an int in the range 0 to 255. If no byte is available because the end of the stream has been reached, the value -1 is returned. This method blocks until input data is available, the end of the stream is detected, or an exception is thrown.
读取字节流数据的next字节。其值是范围从0到255之间的Int类型的数据。如果因为已经达到了流的末尾而没有可以读取的字节,则返回值-1。此方法会阻塞,知道输入数据可用、检测到流结束或抛出异常。
A subclass must provide an implementation of this method.
子类必须实现这个方法。
Returns:
the next byte of data, or -1 if the end of the stream is reached.
Throws:
IOException - if an I/O error occurs.
在计算机中,字节是数据存储的最小单位。而字节(Byte)由8个位(bit)最成。从0000 0000到1111 1111共有256种组合。
程序要处理数据,需要把数据“拿进来”。首先,我们想到java中的byte是8位的数据类型,正好可以“盛载”对应的字节数据。即对应关系如下(注意,byte类型的数据存储时使用补码)
字节数据 | byte值二进制形式 | byte值十进制形式 |
---|---|---|
0000 0000 | 0000 0000 | 0 |
0000 0001 | 0000 0001 | 1 |
… | … | … |
0111 1111 | 0111 1111 | 127 |
1000 0000 | 1000 0000 | - 128 |
1000 0001 | 1000 0001 | -127 |
… | … | … |
1111 1111 | 1111 1111 | -1 |
但是,会存在一个问题,文件(或其他数据源)的结束无法表示。文件的结束跟定不能使用byte类型的某个数字进行表示,因为byte的全部数字都和字节数据进行对应,如果使用了某个数字作为文件的结束,就和数据产生了冲突,程序无法判断该数字代表字节数据还是文件的结束。
于是,我们选择使用int类型的后8(Int类型有32位,使用最后8位)“盛载”对应的字节数据。即对应关系如下:
字节数据 | Int值二进制形式 | Int值十进制形式 |
---|---|---|
0000 0000 | 0000 0000 0000 0000 0000 0000 0000 0000 | 0 |
0000 0001 | 0000 0000 0000 0000 0000 0000 0000 0001 | 1 |
… | … | … |
1111 1111 | 0000 0000 0000 0000 0000 0000 1111 1111 | 255 |
EOF(非字节数据,文件尾标志) | 1111 1111 1111 1111 1111 1111 1111 1111 | -1 |
文件尾EOF(end of file)我们用-1来表示,它没有和任何字节数据冲突,因此,读取一个字节数据,我们使用Int数据类型来“盛载”,是为了文件的结束EOF可以得到表示,并且不和字节数据冲突。
想要了解更多相关知识,请继续阅读扩展知识。
二 扩展知识
文件结尾(英语:End of File,缩写为EOF),是操作系统无法从数据源读取更多数据的情形。数据源通常为文件或流。【百度百科】
简而言之,文件尾标志EOF,就是文件结束的标志,读取文件字节数据时,如果返回EOF,则表示文件已经读取完毕或没有字节数据可供读取。
疑问一: EOF是一个可被插入到文件中的字符(类似换行符被插入到文件中)吗?
测试,我们使用notepad++编辑一个文件,在其中输入“abc”,然后使用工具查看其底层存储对应的十六进制数。
可以看到“abc”三个字符对应的ASCII编码的码点正好是“61 62 63”,并不存在其他字符。
因此,EOF不是一个类似回车换行的字符。
疑问二 EOF不是一个可被插入到文件中字符,它是什么,又如何判断是否读取到了文件尾?
没有EOF这样的字符。操作系统确切地知道文件包含多少字节(这与其他元数据一起存储在内存里,如读写权限,创建日期和名称)。因此操作系统可以告诉尝试读取十字节文件的第十一个字节的程序:你已经到达文件末尾,没有更多的字节可以读取。
告知的方式就是通过返回EOF标志,不同的语言(或平台)EOF标志对应的数值可能不同,通常EOF对应的值是-1。
.
疑问三 数据源除了文件之外,还可能是终端,它如何表示文件尾(即输入结束)?
数据源,即数据的源头,例如文件、磁盘、终端、网络连接等等都是数据源。这里我们重点说明以下我们常用的终端(如Windows的cmd命令行程序)。
从一个终端的输入从来不会真的“结束”(除非设备被断开),但把从终端输入的数据分区成多个“文件”却很有用,因此一个关键的序列被保留下来来指明输入结束。在UNIX和AmigaDOS中,将击键翻译为EOF的过程是由终端的驱动程序完成的,因此应用程序无需将终端和其它输入文件区分开来。Unix平台的驱动程序在行首传送一个传输结束字符(Control-D,ASCII编码为为04)来指明文件结束
在微软的DOS和Windows(以及CP/M和许多DEC操作系统)中,读取数据时终端不会产生EOF。此时,应用程序知道数据源是一个终端(或者其它“字符设备”),并将一个已知的保留的字符或序列解释为文件结束的指明;最普遍地说,它是ASCII码中的替换字符(Control-Z,代码26)
简而言之,对于Windows和DOS,终端(如cmd)不会产生EOF标志,程序知道数据眼是终端并预留一个其他的ASCII控制字符(0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符)),作为EOF的等价/替换字符(或理解为“别名”),这个字符如下图所示:终端输入方式Ctrl + Z
对于UNIX系统而言,终端的驱动程序可以产生EOF标志,因此程序无法判断数据源是文件还是终端。终端输入方式Ctrl + D
总结如下
系统 | 终端输入EOF标志或等价标志 | ASC码十进制 | 十六进制 | 控制字符 | 说明 |
---|---|---|---|---|---|
Windows/DOS | Ctrl + Z | 26 | 1A | SUB | Substitute(替补/替换) |
UNIX系列(Linux等) | Ctrl + Z | 4 | 04 | EOT | End of Transmission(传输结束) |
注释:0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),想了解更过ASCII编码知识,点击【ASCII中的控制字符含义】