當然,我們經常想做的一件事情是將格式化的輸出打印到控制台,但那已在第5章創建的com.bruceeckel.tools中得到了簡化。
第1到第4部分演示了輸入流的創建與使用(盡管第4部分展示了將輸出流作為一個測試工具的簡單應用)。
1. 緩沖的輸入文件
為打開一個文件以便輸入,需要使用一個FileInputStream,同時將一個String或File對象作為文件名使用。為提高速度,最好先對文件進行緩沖處理,從而獲得用於一個BufferedInputStream的構建器的結果句柄。為了以格式化的形式讀取輸入數據,我們將那個結果句柄賦給用於一個DataInputStream的構建器。DataInputStream是我們的最終(final)對象,並是我們進行讀取操作的接口。
在這個例子中,只用到了readLine()方法,但理所當然任何DataInputStream方法都可以采用。一旦抵達文件末尾,readLine()就會返回一個null(空),以便中止並退出while循環。
“String s2”用於聚集完整的文件內容(包括必須添加的新行,因為readLine()去除了那些行)。隨後,在本程序的後面部分中使用s2。最後,我們調用close(),用它關閉文件。從技術上說,會在運行finalize()時調用close()。而且我們希望一旦程序退出,就發生這種情況(無論是否進行垃圾收集)。然而,Java 1.0有一個非常突出的錯誤(Bug),造成這種情況不會發生。在Java 1.1中,必須明確調用System.runFinalizersOnExit(true),用它保證會為系統中的每個對象調用finalize()。然而,最安全的方法還是為文件明確調用close()。
2. 從內存輸入
這一部分采用已經包含了完整文件內容的String s2,並用它創建一個StringBufferInputStream(字串緩沖輸入流)——作為構建器的參數,要求使用一個String,而非一個StringBuffer)。隨後,我們用read()依次讀取每個字符,並將其發送至控制台。注意read()將下一個字節返回為int,所以必須將其造型為一個char,以便正確地打印。
3. 格式化內存輸入
StringBufferInputStream的接口是有限的,所以通常需要將其封裝到一個DataInputStream內,從而增強它的能力。然而,若選擇用readByte()每次讀出一個字符,那麼所有值都是有效的,所以不可再用返回值來偵測何時結束輸入。相反,可用available()方法判斷有多少字符可用。下面這個例子展示了如何從文件中一次讀出一個字符:
//: TestEOF.java // Testing for the end of file while reading // a byte at a time. import java.io.*; public class TestEOF { public static void main(String[] args) { try { DataInputStream in = new DataInputStream( new BufferedInputStream( new FileInputStream("TestEof.java"))); while(in.available() != 0) System.out.print((char)in.readByte()); } catch (IOException e) { System.err.println("IOException"); } } } ///:~
注意取決於當前從什麼媒體讀入,avaiable()的工作方式也是有所區別的。它在字面上意味著“可以不受阻塞讀取的字節數量”。對一個文件來說,它意味著整個文件。但對一個不同種類的數據流來說,它卻可能有不同的含義。因此在使用時應考慮周全。
為了在這樣的情況下偵測輸入的結束,也可以通過捕獲一個違例來實現。然而,若真的用違例來控制數據流,卻顯得有些大材小用。
4. 行的編號與文件輸出
這個例子展示了如何LineNumberInputStream來跟蹤輸入行的編號。在這裡,不可簡單地將所有構建器都組合起來,因為必須保持LineNumberInputStream的一個句柄(注意這並非一種繼承環境,所以不能簡單地將in4造型到一個LineNumberInputStream)。因此,li容納了指向LineNumberInputStream的句柄,然後在它的基礎上創建一個DataInputStream,以便讀入數據。
這個例子也展示了如何將格式化數據寫入一個文件。首先創建了一個FileOutputStream,用它同一個文件連接。考慮到效率方面的原因,它生成了一個BufferedOutputStream。這幾乎肯定是我們一般的做法,但卻必須明確地這樣做。隨後為了進行格式化,它轉換成一個PrintStream。用這種方式創建的數據文件可作為一個原始的文本文件讀取。
標志DataInputStream何時結束的一個方法是readLine()。一旦沒有更多的字串可以讀取,它就會返回null。每個行都會伴隨自己的行號打印到文件裡。該行號可通過li查詢。
可看到用於out1的、一個明確指定的close()。若程序准備掉轉頭來,並再次讀取相同的文件,這種做法就顯得相當有用。然而,該程序直到結束也沒有檢查文件IODemo.txt。正如以前指出的那樣,如果不為自己的所有輸出文件調用close(),就可能發現緩沖區不會得到刷新,造成它們不完整。