Java 中的 PrintStream 與 PrintWriter
一、簡介
在本教程中,我們將比較PrintStream
和PrintWriter
Java 類。本文將幫助程序員為這些類中的每一個找到合適的用例。
在深入了解內容之前,我們建議您查看我們之前的文章,其中我們演示瞭如何使用PrintStream
和PrintWriter
。
2. PrintStream
和PrintWriter
的相似之處
因為PrintStream
和PrintWriter
共享它們的一些功能,所以程序員有時很難為這些類找到合適的用例。讓我們首先確定它們的相似之處;然後,我們將看看差異。
2.1.字符編碼
無論何種系統,字符編碼都允許程序操縱文本,以便跨平台對文本進行一致的解釋。
在 JDK 1.4 版本之後, PrintStream
類在其構造函數中包含了一個字符編碼參數。這允許PrintStream
類在跨平台實現中編碼/解碼文本。另一方面, PrintWriter
從一開始就一直具有字符編碼功能。
我們可以參考官方Java代碼來確認:
public PrintStream(OutputStream out, boolean autoFlush, String encoding) throws UnsupportedEncodingException {
this(requireNonNull(out, "Null output stream"), autoFlush, toCharset(encoding));
}
類似地, PrintWriter
構造函數有一個charset
參數來指定用於編碼目的的Charset
:
public PrintWriter(OutputStream out, boolean autoFlush, Charset charset) {
this(new BufferedWriter(new OutputStreamWriter(out, charset)), autoFlush);
// save print stream for error propagation
if (out instanceof java.io.PrintStream) {
psOut = (PrintStream) out;
}
}
如果沒有為這些類中的任何一個提供字符編碼,它們將使用默認的平台編碼。
2.2.寫入文件
要將文本寫入文件,我們可以將String
或File
實例傳遞給相應的構造函數。另外,我們可以傳遞字符集進行字符編碼。
例如,我們將引用具有單個File
參數的構造函數。在這種情況下,字符編碼將默認為平台:
public PrintStream(File file) throws FileNotFoundException {
this(false, new FileOutputStream(file));
}
同樣, PrintWriter
類有一個構造函數來指定要寫入的文件:
public PrintWriter(File file) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))), false);
}
正如我們所見,這兩個類都提供了寫入文件的功能。但是,它們的實現因使用不同的流父類而有所不同。我們將在本文的differences
部分深入探討為什麼會出現這種情況。
3. PrintStream
和PrintWriter
的區別
在上一節中,我們展示了PrintStream
和PrintWriter
共享一些可能適合我們案例的功能。儘管如此,儘管我們可以對這些類做同樣的事情,但它們的實現各不相同,這使我們能夠評估哪個類更適合。
現在,讓我們看看PrintStream
和PrintWriter
之間的區別。
3.1.數據處理
在上一節中,我們展示了這兩個類如何寫入文件。讓我們看看它們的實現有何不同。
對於PrintStream
,它是OutputStream
的子類,在 Java 中定義為字節流。換句話說,數據是逐字節處理的。另一方面, PrintWriter
是一個字符流,它一次處理每個字符,並使用 Unicode 自動轉換我們指定的每個字符集。
我們將在兩種不同的情況下展示這些實現中的每一個。
3.2.處理非文本數據
因為這兩個類處理數據的方式不同,所以我們在處理非文本文件的時候可以具體區分一下。在此示例中,我們將使用 png 文件讀取數據,然後在將其內容寫入每個類的另一個文件後查看差異:
public class DataStream {
public static void main (String[] args) throws IOException {
FileInputStream inputStream = new FileInputStream("image.png");
PrintStream printStream = new PrintStream("ps.png");
int b;
while ((b = inputStream.read()) != -1) {
printStream.write(b);
}
printStream.close();
FileReader reader = new FileReader("image.png");
PrintWriter writer = new PrintWriter("pw.png");
int c;
while ((c = reader.read()) != -1) {
writer.write(c);
}
writer.close();
}
}
在這個例子中,我們使用FileInputStream
和FileReader
來讀取圖像的內容。然後,我們將數據寫入不同的輸出文件。
因此, ps.png
和pw.png
文件將根據流處理其內容的方式包含數據。我們知道PrintStream
通過一次讀取一個字節來處理數據。因此,生成的文件包含與原始文件相同的原始數據。
與PrintStream
類不同, PrintWriter
將數據解釋為字符。**這會導致系統無法理解其內容的損壞文件**。或者,我們可以將pw.txt
pw.png
並檢查PrintWriter
如何嘗試將圖像的原始數據轉換為難以辨認的符號。
3.3.處理文本數據
現在,讓我們看一個示例,其中我們使用OutputStream
( PrintStream
的父類)來演示在寫入文件時如何處理字符串:
public class PrintStreamWriter {
public static void main (String[] args) throws IOException {
OutputStream out = new FileOutputStream("TestFile.txt");
out.write("foobar");
out.flush();
}
}
上面的代碼將無法編譯,因為OutputStream
不知道如何處理字符串。**要成功寫入文件,輸入數據必須是原始字節序列。**以下更改將使我們的代碼成功寫入文件:
out.write("foobar".getBytes());
回到PrintStream
,雖然這個類是OutputStream
的子類,但Java 在內部調用了getBytes()
方法。這允許PrintStream
在調用print
方法時接受字符串.
讓我們看一個例子:
public class PrintStreamWriter {
public static void main (String[] args) throws IOException {
PrintStream out = new PrintStream("TestFile.txt");
out.print("Hello, world!");
out.flush();
}
}
現在,因為PrintWriter
知道如何處理字符串,我們調用傳遞字符串輸入的print
方法。然而,在這種情況下, Java 並沒有將字符串轉換為字節,而是在內部將流中的每個字符轉換為其對應的 Unicode 編碼:
public class PrintStreamWriter {
public static void main (String[] args) throws IOException {
PrintWriter out = new PrintWriter("TestFile.txt");
out.print("Hello, world!");
out.flush();
}
}
基於這些類在內部處理文本數據的方式,字符流類(如PrintWriter
)在對文本進行 I/O 操作時可以更好地處理這些內容。此外,在本地字符集的編碼過程中將數據翻譯成 Unicode 使應用程序的國際化更簡單。
3.4.法拉盛
在我們前面的示例中,請注意我們必須如何顯式調用flush
方法.
根據 Java 文檔,這個過程在這兩個類之間的工作方式不同。
對於PrintStream,
我們可以指定僅在寫入字節數組、調用println
方法或寫入換行符時自動刷新。然而, PrintWriter
也可以有自動刷新,但只有當我們調用println, printf,
或format
方法時。
這種區別很難證明,因為文檔提到在上述情況下會發生刷新,但沒有提到何時不會發生。因此,我們可以演示自動刷新在這兩個類中是如何工作的,但我們不能保證它會按預期運行。
在此示例中,我們將啟用自動刷新功能並在末尾寫入一個帶有換行符的字符串:
public class AutoFlushExample {
public static void main (String[] args) throws IOException {
PrintStream printStream = new PrintStream(new FileOutputStream("autoFlushPrintStream.txt"), true);
printStream.write("Hello, world!\n".getBytes());
PrintWriter printWriter = new PrintWriter(new FileOutputStream("autoFlushPrintWriter.txt"), true);;
printWriter.print("Hello, world!");
}
}
確保文件autoFlushPrintStream.txt
將包含寫入文件的內容,因為我們啟用了自動刷新功能。此外,我們使用包含換行符的字符串調用write
方法以強制刷新。
但是,我們希望看到autoFlushPrintWriter.txt
文件為空,儘管這不能保證。畢竟,刷新可能發生在程序執行期間。
如果我們想在使用PrintWriter,
代碼必須滿足我們上面提到的所有要求,或者我們可以添加一行代碼來顯式刷新 writer:
printWriter.flush();
4。結論
在本文中,我們比較了兩個數據流類PrintStream
和PrintWriter
。首先,我們研究了它們的相似性和使用本地字符集的能力。此外,我們還介紹瞭如何讀取和寫入外部文件的示例。雖然我們可以用這兩個類實現類似的事情,但在查看差異之後,我們證明了每個類在不同場景中的表現更好。
例如,我們在寫入所有類型的數據時受益於PrintStream
,因為PrintStream
處理原始字節。另一方面,作為字符流的PrintWriter
在執行 I/O 操作時最適合文本。此外,由於其 Unicode 內部格式,它有助於復雜的軟件實現,例如國際化。最後,我們比較了刷新實現在兩個類中的不同之處。
與往常一樣,本文的完整代碼可在 GitHub 上獲得。