Java GBK 中文亂碼問題分析

amadan發表於2021-09-09

在io相關的操作中經常會出現亂碼問題

比如在一個txt檔案中按GBK編碼儲存內容”淘!我喜歡!”

Java GBK 中文亂碼問題分析

然後用RandomAccessFile類讀取並列印一行。

RandomAccessFile raf = new RandomAccessFile("D://1.txt","r");
System.out.print(raf.readLine());

列印結果顯示亂碼:

Java GBK 中文亂碼問題分析

在網上查詢到加入相關編碼解碼操作後可以解決該問題

RandomAccessFile raf = new RandomAccessFile("D://1.txt","r");
System.out.print(new String(raf.readLine().getBytes("ISO-8859-1"),"gbk"));

Java GBK 中文亂碼問題分析

問題:

在這個過程中發生了什麼?

要解答這個問題首先要知道編碼和解碼的概念以及產生的原因:

為什麼要編碼

不知道大家有沒有想過一個問題,那就是為什麼要編碼?我們能不能不編碼?要回答這個問題必須要回到計算機是如何表示我們人類能夠理解的符號的,這些符號也就是我們人類使用的語言。由於人類的語言有太多,因而表示這些語言的符號太多,無法用計算機中一個基本的儲存單元—— byte 來表示,因而必須要經過拆分或一些翻譯工作,才能讓計算機能理解。我們可以把計算機能夠理解的語言假定為英語,其它語言要能夠在計算機中使用必須經過一次 翻譯,把它翻譯成英語。這個翻譯的過程就是編碼。所以可以想象只要不是說英語的國家要能夠使用計算機就必須要經過編碼。這看起來有些霸道,但是這就是現 狀,這也和我們國家現在在大力推廣漢語一樣,希望其它國家都會說漢語,以後其它的語言都翻譯成漢語,我們可以把計算機中儲存資訊的最小單位改成漢字,這樣 我們就不存在編碼問題了。

所以總的來說,編碼的原因可以總結為:

1. 計算機中儲存資訊的最小單元是一個位元組即 8 個 bit,所以能表示的字元範圍是 0~255 個。

2. 人類要表示的符號太多,無法用一個位元組來完全表示。

3. 要解決這個矛盾必須需要一個新的資料結構 char,從 char 到 byte 必須編碼。

名詞解釋:

解碼:將byte陣列轉為char陣列。

編碼:將char陣列轉為byte陣列。

計算機儲存的基本單位是byte,但開啟一個檔案時檔案編輯器已經做了解碼的工作。

以下為解碼過程描述

Java GBK 中文亂碼問題分析

檔案實際儲存的內容是(以下為 16 進位制):

Java GBK 中文亂碼問題分析

開啟檔案後看到的內容為

Java GBK 中文亂碼問題分析

需要詳細說明以下程式碼的處理過程

RandomAccessFile raf= new RandomAccessFile("D://1.txt","r");
System.out.print(raf.readLine());

首先看一下java.io.RandomAccessFile#readLine方法的原始碼

public final String readLine() throws IOException {
        StringBuffer input = new StringBuffer();
        int c = -1;
        boolean eol = false;
        while (!eol) {
            switch (c = read()) {
            case -1:
            case '/n':
                eol = true;
                break;
            case '/r':
                eol = true;
                long cur = getFilePointer();
                if ((read()) != '/n') {
                    seek(cur);
                }
                break;
            default:
                input.append((char)c);
                break;
            }
        }
        if ((c == -1) && (input.length() == 0)) {
            return null;
        }
        return input.toString();
    }

主要關注read()部分和(char)c,read()是一個本地方法,作用是從檔案中讀取一個byte位元組。

Java GBK 中文亂碼問題分析

(char)c是將變數c從byte型別轉換為char型別,這是一個解碼操作。

問題:此處是以什麼格式進行解碼?

解碼格式是ISO-8859-1

raf.readLine()的處理過程如下

Java GBK 中文亂碼問題分析

那麼

new String(raf.readLine().getBytes("ISO-8859-1"),"gbk")

這行程式碼做了什麼

首先readLine()按行一位元組一位元組地讀取檔案中的資料,並且按ISO-8859-1進行解碼拼成char陣列,然後getBytes(“ISO-8859-1″)對拼成後的char陣列按ISO-8859-1進行編碼獲取byte陣列,最後new String(string,”gbk”)對編碼後的byte陣列用gbk格式進行解碼成char陣列。

參考資料: 深入分析 Java 中的中文編碼問題

遺留問題:

1 、如何避免重複轉碼。

2 、RandomAccessFile的readLine () 效率非常低,如何提高效率。

相關文章