在Java中,能够读取一个字节序列的对象就称作一个输入流(Input Stream)。
定义
在Java中,我们把能够读取一个字节序列的对象称作一个输入流;而我们把能够写一个字节序列的对象称作一个输出流。它们分别由抽象类InputStream和OutputStream类表示。
教材截取
当然,我们经常想做的一件事情是将格式化的输出打印到控制台,但那已在第5章创建的com.bruceeckel.tools中得到了简化。
第1到第4部分演示了输入流的创建与使用(尽管第4部分展示了将输出流作为一个测试工具的简单应用)。
1.缓冲的输入文件
为打开一个文件以便输入,需要使用一个FileInputStream,同时将一个String或File对象作为文件名使用。为提高速度,最好先对文件进行缓冲处理,从而获得用于一个BufferedInputStream的构建器的结果句柄。为了以格式化的形式读取输入数据,我们将那个结果句柄赋给用于一个DataInputStream的构建器。DataInputStream是我们的最终(final)对象,并是我们进行读取操作的接口。
在这个例子中,只用到了readLine方法,但理所当然任何DataInputStream方法都可以采用。一旦抵达文件末尾,readLine就会返回一个null(空),以便中止并退出while循环。
“Strings2”用于聚集完整的文件内容(包括必须添加的新行,因为readLine去除了那些行)。随后,在本程序的后面部分中使用s2。最后,我们调用close,用它关闭文件。从技术上说,会在运行finalize时调用close。而且我们希望一旦程序退出,就发生这种情况(无论是否进行垃圾收集)。然而,Java1.0有一个非常突出的错误(Bug),造成这种情况不会发生。在Java1.1中,必须明确调用System.runFinalizersOnExit(true),用它保证会为系统中的每个对象调用finalize。然而,最安全的方法还是为文件明确调用close。
2.从内存输入
这一部分采用已经包含了完整文件内容的Strings2,并用它创建一个StringBufferInputStream(字串缓冲输入流)——作为构建器的参数,要求使用一个String,而非一个StringBuffer。随后,我们用read依次读取每个字符,并将其发送至控制台。注意read将下一个字节返回为int,所以必须将其造型为一个char,以便正确地打印。
3.格式化内存输入
StringBufferInputStream的接口是有限的,所以通常需要将其封装到一个DataInputStream内,从而增强它的能力。然而,若选择用readByte每次读出一个字符,那么所有值都是有效的,所以不可再用返回值来侦测何时结束输入。相反,可用available方法判断有多少字符可用。下面这个例子展示了如何从文件中一次读出一个字符:
注意取决于当前从什么媒体读入,avaiable的工作方式也是有所区别的。它在字面上意味着“可以不受阻塞读取的字节数量”。对一个文件来说,它意味着整个文件。但对一个不同种类的数据流来说,它却可能有不同的含义。因此在使用时应考虑周全。
为了在这样的情况下侦测输入的结束,也可以通过捕获一个违例来实现。然而,若真的用违例来控制数据流,却显得有些大材小用。
4.行的编号与文件输出
这个例子展示了如何LineNumberInputStream来跟踪输入行的编号。在这里,不可简单地将所有构建器都组合起来,因为必须保持LineNumberInputStream的一个句柄(注意这并非一种继承环境,所以不能简单地将in4造型到一个LineNumberInputStream)。因此,li容纳了指向LineNumberInputStream的句柄,然后在它的基础上创建一个DataInputStream,以便读入数据。
这个例子也展示了如何将格式化数据写入一个文件。首先创建了一个FileOutputStream,用它同一个文件连接。考虑到效率方面的原因,它生成了一个BufferedOutputStream。这几乎肯定是我们一般的做法,但却必须明确地这样做。随后为了进行格式化,它转换成一个PrintStream。用这种方式创建的数据文件可作为一个原始的文本文件读取。
标志DataInputStream何时结束的一个方法是readLine。一旦没有更多的字串可以读取,它就会返回null。每个行都会伴随自己的行号打印到文件里。该行号可通过li查询。
可看到用于out1的、一个明确指定的close。若程序准备掉转头来,并再次读取相同的文件,这种做法就显得相当有用。然而,该程序直到结束也没有检查文件IODemo.txt。正如以前指出的那样,如果不为自己的所有输出文件调用close,就可能发现缓冲区不会得到刷新,造成它们不完整。
缓冲区
调用iostream输入数据时,若用户输入的数据类型与要求的不符(比如要求读入整数而用户输入的不是数字),则cin会被设置为fail(可以用!cin判断),而用户输入的数据仍然留在流缓冲区里,需要手动清空:
cin.clear;
cin.ignore(numeric_limits::max,'
');