Java GBK 中文亂碼問題分析

java001 8年前發布 | 23K 次閱讀 Java Java開發

來自: http://my.oschina.net/skyshitt/blog/614439?fromerr=XWpdlklM

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

比如在一個txt文件中按GBK編碼保存內容"淘!我喜歡!"

然后用RandomAccessFile類讀取并打印一行。

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

打印結果顯示亂碼:

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

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

問題:

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

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

為什么要編碼

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

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

1. 計算機中存儲信息的最小單元是一個字節即 8 個 bit,所以能表示的字符范圍是 0~255 個。

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

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

名詞解釋:

解碼:將byte數組轉為char數組。

編碼:將char數組轉為byte數組。

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

以下為解碼過程描述

文件實際存儲的內容是(以下為 16 進制):

打開文件后看到的內容為

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

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字節。

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

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

解碼格式是ISO-8859-1

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

那么

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 () 效率非常低,如何提高效率。

 本文由用戶 java001 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!