java IO流之轉(zhuǎn)換流的具體使用
簡介
轉(zhuǎn)換流可以將一個字節(jié)流包裝成字符流,或者將一個字符流包裝成字節(jié)流。這種轉(zhuǎn)換通常用于處理文本數(shù)據(jù),如讀取文本文件或?qū)?shù)據(jù)從網(wǎng)絡(luò)傳輸?shù)綉?yīng)用程序。
轉(zhuǎn)換流主要有兩種類型:InputStreamReader 和 OutputStreamWriter。
InputStreamReader 將一個字節(jié)輸入流轉(zhuǎn)換為一個字符輸入流,而 OutputStreamWriter 將一個字節(jié)輸出流轉(zhuǎn)換為一個字符輸出流。它們使用指定的字符集將字節(jié)流和字符流之間進行轉(zhuǎn)換。常用的字符集包括 UTF-8、GBK、ISO-8859-1 等。
一、編碼和解碼
在計算機中,數(shù)據(jù)通常以二進制形式存儲和傳輸。
- 編碼就是將原始數(shù)據(jù)(比如說文本、圖像、視頻、音頻等)轉(zhuǎn)換為二進制形式。
- 解碼就是將二進制數(shù)據(jù)轉(zhuǎn)換為原始數(shù)據(jù),是一個反向的過程。
常見的編碼和解碼方式有很多,舉幾個例子:
- ASCII 編碼和解碼:在計算機中,常常使用 ASCII 碼來表示字符,如鍵盤上的字母、數(shù)字和符號等。例如,字母 A 對應(yīng)的 ASCII 碼是 65,字符 + 對應(yīng)的 ASCII 碼是 43。
- Unicode 編碼和解碼:Unicode 是一種字符集,支持多種語言和字符集。在計算機中,Unicode 可以使用 UTF-8、UTF-16 等編碼方式將字符轉(zhuǎn)換為二進制數(shù)據(jù)進行存儲和傳輸。
- Base64 編碼和解碼:Base64 是一種將二進制數(shù)據(jù)轉(zhuǎn)換為 ASCII 碼的編碼方式。它將 3 個字節(jié)的二進制數(shù)據(jù)轉(zhuǎn)換為 4 個 ASCII 字符,以便在網(wǎng)絡(luò)傳輸中使用。例如,將字符串 “Hello, world!” 進行 - Base64 編碼后,得到的結(jié)果是 “SGVsbG8sIHdvcmxkIQ==”。
- 圖像編碼和解碼:在圖像處理中,常常使用 JPEG、PNG、GIF 等編碼方式將圖像轉(zhuǎn)換為二進制數(shù)據(jù)進行存儲和傳輸。在解碼時,可以將二進制數(shù)據(jù)轉(zhuǎn)換為圖像,以便顯示或處理。
- 視頻編碼和解碼:在視頻處理中,常常使用 H.264、AVC、MPEG-4 等編碼方式將視頻轉(zhuǎn)換為二進制數(shù)據(jù)進行存儲和傳輸。在解碼時,可以將二進制數(shù)據(jù)轉(zhuǎn)換為視頻,以便播放或處理。
簡單一點說就是:
- 編碼:字符(能看懂的)–>字節(jié)(看不懂的)
- 解碼:字節(jié)(看不懂的)–>字符(能看懂的)
String str = "追風少年"; String charsetName = "UTF-8"; // 編碼 byte[] bytes = str.getBytes(Charset.forName(charsetName)); System.out.println("編碼: " + bytes); // 解碼 String decodedStr = new String(bytes, Charset.forName(charsetName)); System.out.println("解碼: " + decodedStr);
在這個示例中,首先定義了一個字符串變量 str 和一個字符集名稱 charsetName。然后,使用 Charset.forName() 方法獲取指定字符集的 Charset 對象。接著,使用字符串的 getBytes() 方法將字符串編碼為指定字符集的字節(jié)數(shù)組。最后,使用 new String() 方法將字節(jié)數(shù)組解碼為字符串。
需要注意的是,在編碼和解碼過程中,要保證使用相同的字符集,以便正確地轉(zhuǎn)換數(shù)據(jù)。
二、字符集
Charset:字符集,是一組字符的集合,每個字符都有一個唯一的編碼值,也稱為碼點。
常見的字符集包括 ASCII、Unicode 和 GBK,而 Unicode 字符集包含了多種編碼方式,比如說 UTF-8、UTF-16。
2.1ASCII 字符集
ASCII(American Standard Code for Information Interchange,美國信息交換標準代碼)字符集是一種最早的字符集,包含 128 個字符,其中包括控制字符、數(shù)字、英文字母以及一些標點符號。ASCII 字符集中的每個字符都有一個唯一的 7 位二進制編碼(由 0 和 1 組成),可以表示為十進制數(shù)或十六進制數(shù)。
ASCII 編碼方式是一種固定長度的編碼方式,每個字符都使用 7 位二進制編碼來表示。ASCII 編碼只能表示英文字母、數(shù)字和少量的符號,不能表示其他語言的文字和符號,因此在全球范圍內(nèi)的應(yīng)用受到了很大的限制。
2.2Unicode 字符集
Unicode 包含了世界上幾乎所有的字符,用于表示人類語言、符號和表情等各種信息。Unicode 字符集中的每個字符都有一個唯一的碼點(code point),用于表示該字符在字符集中的位置,可以用十六進制數(shù)表示。
為了在計算機中存儲和傳輸 Unicode 字符集中的字符,需要使用一種編碼方式。UTF-8、UTF-16 和 UTF-32 都是 Unicode 字符集的編碼方式,用于將 Unicode 字符集中的字符轉(zhuǎn)換成字節(jié)序列,以便于存儲和傳輸。它們的差別在于使用的字節(jié)長度不同。
UTF-8 是一種可變長度的編碼方式,對于 ASCII 字符(碼點范圍為 0x00~0x7F),使用一個字節(jié)表示,對于其他 Unicode 字符,使用兩個、三個或四個字節(jié)表示。UTF-8 編碼方式被廣泛應(yīng)用于互聯(lián)網(wǎng)和計算機領(lǐng)域,因為它可以有效地壓縮數(shù)據(jù),適用于網(wǎng)絡(luò)傳輸和存儲。
UTF-16 是一種固定長度的編碼方式,對于基本多語言平面(Basic Multilingual Plane,Unicode 字符集中的一個碼位范圍,包含了世界上大部分常用的字符,總共包含了超過 65,000 個碼位)中的字符(碼點范圍為 0x0000~0xFFFF),使用兩個字節(jié)表示,對于其他 Unicode 字符,使用四個字節(jié)表示。
UTF-32 是一種固定長度的編碼方式,對于所有 Unicode 字符,使用四個字節(jié)表示。
2.3GBK 字符集
GBK 包含了 GB2312 字符集中的字符,同時還擴展了許多其他漢字字符和符號,共收錄了 21,913 個字符。GBK 采用雙字節(jié)編碼方式,每個漢字占用 2 個字節(jié),其中高字節(jié)和低字節(jié)都使用了 8 位,因此 GBK 編碼共有 2^16=65536 種可能的編碼,其中大部分被用于表示漢字字符。
GBK 編碼是一種變長的編碼方式,對于 ASCII 字符(碼位范圍為 0x00 到 0x7F),使用一個字節(jié)表示,對于其他字符,使用兩個字節(jié)表示。GBK 編碼中的每個字節(jié)都可以采用 0x81 到 0xFE 之間的任意一個值,因此可以表示 2^15=32768 個字符。為了避免與 ASCII 碼沖突,GBK 編碼的第一個字節(jié)采用了 0x81 到 0xFE 之間除了 0x7F 的所有值,第二個字節(jié)采用了 0x40 到 0x7E 和 0x80 到 0xFE 之間的所有值,共 94 個值。
GB2312 的全名是《信息交換用漢字編碼字符集基本集》,也被稱為“國標碼”。采用了雙字節(jié)編碼方式,每個漢字占用 2 個字節(jié),其中高字節(jié)和低字節(jié)都使用了 8 位,因此 GB2312 編碼共有 2^16=65536 種可能的編碼,其中大部分被用于表示漢字字符。GB2312 編碼中的每個字節(jié)都可以采用 0xA1 到 0xF7 之間的任意一個值,因此可以表示 126 個字符。
GB2312 是一個較為簡單的字符集,只包含了常用的漢字和符號,因此對于一些較為罕見的漢字和生僻字,GB2312 不能滿足需求,現(xiàn)在已經(jīng)逐漸被 GBK、GB18030 等字符集所取代。
GB18030 是最新的中文碼表。收錄漢字 70244 個,采用多字節(jié)編碼,每個字可以由 1 個、2 個或 4 個字節(jié)組成。支持中國國內(nèi)少數(shù)民族的文字,同時支持繁體漢字以及日韓漢字等。
三、亂碼
當使用不同的編碼方式讀取或者寫入文件時,就會出現(xiàn)亂碼問題,來看示例。
String s = "追風少年!"; try { // 將字符串按GBK編碼方式保存到文件中 OutputStreamWriter out = new OutputStreamWriter( new FileOutputStream("logs/test_utf8.txt"), "GBK"); out.write(s); out.close(); FileReader fileReader = new FileReader("logs/test_utf8.txt"); int read; while ((read = fileReader.read()) != -1) { System.out.print((char)read); } fileReader.close(); } catch (IOException e) { e.printStackTrace(); }
在上面的示例代碼中,首先定義了一個包含中文字符的字符串,然后將該字符串按 GBK 編碼方式保存到文件中,接著將文件按默認編碼方式(UTF-8)讀取,并顯示內(nèi)容。此時就會出現(xiàn)亂碼問題,顯示為“?????????”。
這是因為文件中的 GBK 編碼的字符在使用 UTF-8 編碼方式解析時無法正確解析,從而導致出現(xiàn)亂碼問題。
那如何才能解決亂碼問題呢?
這就引出我們今天的主角了——轉(zhuǎn)換流。
四、InputStreamReader
java.io.InputStreamReader 是 Reader 類的子類。它的作用是將字節(jié)流(InputStream)轉(zhuǎn)換為字符流(Reader),同時支持指定的字符集編碼方式,從而實現(xiàn)字符流與字節(jié)流之間的轉(zhuǎn)換。
4.1構(gòu)造方法
- InputStreamReader(InputStream in): 創(chuàng)建一個使用默認字符集的字符流。
- InputStreamReader(InputStream in, String charsetName): 創(chuàng)建一個指定字符集的字符流。
代碼示例如下:
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt")); InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
4.2解決編碼問題
下面是一個使用 InputStreamReader 解決亂碼問題的示例代碼:
String s = "沉默王二!"; try { // 將字符串按GBK編碼方式保存到文件中 OutputStreamWriter outUtf8 = new OutputStreamWriter( new FileOutputStream("logs/test_utf8.txt"), "GBK"); outUtf8.write(s); outUtf8.close(); // 將字節(jié)流轉(zhuǎn)換為字符流,使用GBK編碼方式 InputStreamReader isr = new InputStreamReader(new FileInputStream("logs/test_utf8.txt"), "GBK"); // 讀取字符流 int c; while ((c = isr.read()) != -1) { System.out.print((char) c); } isr.close(); } catch (IOException e) { e.printStackTrace(); }
由于使用了 InputStreamReader 對字節(jié)流進行了編碼方式的轉(zhuǎn)換,因此在讀取字符流時就可以正確地解析出中文字符,避免了亂碼問題。
五、OutputStreamWriter
java.io.OutputStreamWriter 是 Writer 的子類,字面看容易誤以為是轉(zhuǎn)為字符流,其實是將字符流轉(zhuǎn)換為字節(jié)流,是字符流到字節(jié)流的橋梁。
- OutputStreamWriter(OutputStream in): 創(chuàng)建一個使用默認字符集的字符流。
- OutputStreamWriter(OutputStream in, String charsetName):創(chuàng)建一個指定字符集的字符流。
代碼示例如下:
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("a.txt")); OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("b.txt") , "GBK");
通常為了提高讀寫效率,我們會在轉(zhuǎn)換流上再加一層緩沖流,來看代碼示例:
try { // 從文件讀取字節(jié)流,使用UTF-8編碼方式 FileInputStream fis = new FileInputStream("test.txt"); // 將字節(jié)流轉(zhuǎn)換為字符流,使用UTF-8編碼方式 InputStreamReader isr = new InputStreamReader(fis, "UTF-8"); // 使用緩沖流包裝字符流,提高讀取效率 BufferedReader br = new BufferedReader(isr); // 創(chuàng)建輸出流,使用UTF-8編碼方式 FileOutputStream fos = new FileOutputStream("output.txt"); // 將輸出流包裝為轉(zhuǎn)換流,使用UTF-8編碼方式 OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8"); // 使用緩沖流包裝轉(zhuǎn)換流,提高寫入效率 BufferedWriter bw = new BufferedWriter(osw); // 讀取輸入文件的每一行,寫入到輸出文件中 String line; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); // 每行結(jié)束后寫入一個換行符 } // 關(guān)閉流 br.close(); bw.close(); } catch (IOException e) { e.printStackTrace(); }
在上面的示例代碼中,首先使用 FileInputStream 從文件中讀取字節(jié)流,使用 UTF-8 編碼方式進行讀取。然后,使用 InputStreamReader 將字節(jié)流轉(zhuǎn)換為字符流,使用 UTF-8 編碼方式進行轉(zhuǎn)換。接著,使用 BufferedReader 包裝字符流,提高讀取效率。然后,創(chuàng)建 FileOutputStream 用于輸出文件,使用 UTF-8 編碼方式進行創(chuàng)建。接著,使用 OutputStreamWriter 將輸出流轉(zhuǎn)換為字符流,使用 UTF-8 編碼方式進行轉(zhuǎn)換。最后,使用 BufferedWriter 包裝轉(zhuǎn)換流,提高寫入效率。
六、小結(jié)
InputStreamReader 和 OutputStreamWriter 是將字節(jié)流轉(zhuǎn)換為字符流或者將字符流轉(zhuǎn)換為字節(jié)流。通常用于解決字節(jié)流和字符流之間的轉(zhuǎn)換問題,可以將字節(jié)流以指定的字符集編碼方式轉(zhuǎn)換為字符流,或者將字符流以指定的字符集編碼方式轉(zhuǎn)換為字節(jié)流。
InputStreamReader 類的常用方法包括:
- read():從輸入流中讀取一個字符的數(shù)據(jù)。
- read(char[] cbuf, int off, int len):從輸入流中讀取 len 個字符的數(shù)據(jù)到指定的字符數(shù)組 cbuf 中,從 off 位置開始存放。
- ready():返回此流是否已準備好讀取。
- close():關(guān)閉輸入流。
OutputStreamWriter 類的常用方法包括:
- write(int c):向輸出流中寫入一個字符的數(shù)據(jù)。
- write(char[] cbuf, int off, int len):向輸出流中寫入指定字符數(shù)組 cbuf 中的 len 個字符,從 off 位置開始。
- flush():將緩沖區(qū)的數(shù)據(jù)寫入輸出流中。
- close():關(guān)閉輸出流。
在使用轉(zhuǎn)換流時,需要指定正確的字符集編碼方式,否則可能會導致數(shù)據(jù)讀取或?qū)懭氤霈F(xiàn)亂碼。
相關(guān)文章鏈接:
到此這篇關(guān)于java IO流之轉(zhuǎn)換流的具體使用的文章就介紹到這了,更多相關(guān)java 轉(zhuǎn)換流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot配置SSL同時支持http和https訪問實現(xiàn)
本文主要介紹了SpringBoot配置SSL同時支持http和https訪問實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07Java8中AbstractExecutorService與FutureTask源碼詳解
這篇文章主要給大家介紹了關(guān)于Java8中AbstractExecutorService與FutureTask的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2022-01-01Java基礎(chǔ)之Bean的創(chuàng)建、定位和使用
這篇文章主要介紹了Java基礎(chǔ)之Bean的創(chuàng)建、定位和使用,文中有非常詳細的圖文示例及代碼,對正在學習java基礎(chǔ)的小伙伴們有很好地幫助,需要的朋友可以參考下2021-05-05