編碼問題之轉碼

這幾天遇到編碼問題,初步整理一下。

計算機中有好多編碼,編碼問題也是因爲文字的編碼不統一,編碼間的差異造成的。

所謂亂碼,其實就是因爲字符使用的編碼,解碼的類型不一樣,纔出現的一些不認識或者不是想要的數據。

比如:用UTF-8編碼的【中國】 換成GBK 解碼時會出現【涓湅】;換成GB2312 解碼時【涓��】……


一、那我們首先看看有多少編碼類型:

1、ASCII 碼 :

一共有128種字符編碼,是對英文字符(字母+符號)和二進制統一的編碼,計算機裏是用二進制位進行通信的,所以這一套ASCII 碼使英文字符在計算機裏基本上可以直接使用了,着實給使用英語爲母語的國家帶來了很大的方便。

2、Unicode 編碼:

Unicode只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲。而且Unicode 有多種存儲方式,使用起來不是很方便。

其實Unicode 對我們判斷使用漢語的國家人民還有另一種不便,我們如果判斷漢語是不是繁體 或是簡體的話 其實就很麻煩了,因爲Unicode 存儲漢語是有一定的範圍的,可是繁簡體之間是混插的 沒有界限的,所以需要自己轉碼進行辨別。

3、UTF-8:

UTF-8 其實是Unicode 的一種實現方式(還有UTF-16等等)。在互聯網中使用的非常廣。因爲UTF-8 編碼可以使整個互聯網中實現同意編碼,儘量的減少或避免因爲編碼的差異造成的不必要的麻煩。

UTF-8最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個字節表示一個符號,根據不同的符號而變化字節長度。

UTF-8的編碼規則:
1)如果是 單字節的符號,那麼 令字節的第一位設爲0,後面7位爲這個符號的unicode碼。對於英語字母來說,UTF-8編碼和ASCII碼是相同的。
2)如果是 n 字節的符號(n>1),第一個字節的前面的 n位都設爲1,第n+1位設爲0,後面的字節的前兩位均爲10。其他的二進制位是由這個字符的 Unicode 碼 按照順序進行填充而得。
如下表:
            Unicode符號範圍 | UTF-8編碼方式
                      (十六進制)   | (二進制)
----------------------------------+---------------------------------------------
0000 0000-0000 007F    |      0xxxxxxx
0000 0080-0000 07FF    |     110xxxxx 10xxxxxx
0000 0800-0000 FFFF    |     1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF    |     11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 

4、ISO-8859-1 :

因爲除了應該還有其他語言,尤其是西歐 比如法語 會有重音字符,這就造就了ISO-8859-1 編碼,ISO-8859-1 是國際標準化組織內ISO/IEC 8859的第一個8位字符集,它以ASCII爲基礎,在空置的0xA0-0xFF的範圍內,加入96個字母及符號,藉以供使用變音符號的拉丁字母語言使用。所以又稱Latin-1或“西歐語言”。

5、GB2313 ,Big5,GBK

這幾種是正對使用漢字或者類漢字的字符的編碼方式。

①GB2312 碼 :

兼容ASCII 碼  共收入漢字6763個和非漢字圖形字符682個。整個字符集分成94個區,每區有94個位。每個區位上只有一個字符,因此可用所在的區和位來對漢字進行編碼,稱爲區位碼。其中主要是簡體中文 和 部分繁體中文及日韓的一些漢字。

②Big5 (大5碼):

主要是港澳臺使用的繁體字的編碼集。

③GBK :

它是GB2312的擴展,加入對繁體字的支持,兼容GB2312。字符有一字節和雙字節編碼,00–7F範圍內是一位,和ASCII保持一致,此範圍內嚴格上說有96個文字和32個控制符號。之後的雙字節中,前一字節是雙字節的第一位。總體上說第一字節的範圍是81–FE(也就是不含80和FF),第二字節的一部分領域在40–FE,其他領域在80–FE。

6、其他的不常用 不再介紹

二、編碼之間的轉換:

如果字符用一種編碼方式編碼,再用同一樣的編碼方式解碼,那麼輸出的字符是正常的;要是解碼時用的是另一中編碼則會出現亂碼。

那麼我們用代碼試試:

public static void main (String[] args) throws java.lang.Exception{
       String  str = new String("我".getBytes("UTF-8"),"GB2312");
       String  str2 = new String("我".getBytes("UTF-8"),"UTF-8"); 
       System.out.println("str: "+str+"  "+"  str2: "+str2);
	}
執行的結果:

str: ��    str2: 我


這結果可以有力證明亂碼的出現 和 其解決辦法。

三、分辨編碼範圍:

若只判斷中文字符時可以 判斷是不是屬於 CJK(中日韓統一表意文字):

public static boolean isChinese(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
            return true;
        }
        return false;
    }

那麼判斷文簡體還是繁體呢?  我覺得還是得判斷編碼範圍了:

思路是 當文字是在GB2312 範圍內 時我們認爲是簡體中文 ,若在Big5 範圍內時 是繁體。如下:

 private static final String gbEncode = "GB2312";
 private static final String big5Encode = "Big5";

    public static boolean isJTChinese(Character c) {
        String str = c.toString();
        try {
            if (str.equals(new String(str.getBytes(gbEncode), gbEncode))) {
                System.out.println(c+"是簡體");
                return true;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }

    public static boolean isFTChinese(Character c) {
        String str = c.toString();
        try {
            if (str.equals(new String(str.getBytes(big5Encode), big5Encode))) {
                System.out.println(c+"是繁體");
                return true;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }

其他的問題同理可以得到答案。

四、第三方庫

現成的一個庫可以使用 :language-detection。      language-detection 項目鏈接  https://code.google.com/p/language-detection/

此工具是一個使用java實現的語言檢測工具,使用的是樸素貝葉斯方法,對噪聲少的語言句子,準確率能達到99%,目前已經能夠支持53種語言。

使用非常方便,只要把下載下來的2個jar 導入工程,然後把含有語言包的 profiles 屬性文件加載進去即可使用。

測試代碼如下:

import com.cybozu.labs.langdetect.Detector;
import com.cybozu.labs.langdetect.DetectorFactory;
import com.cybozu.labs.langdetect.LangDetectException;

public class LangTest {

    public static void main(String[] args) {
        Detector detect;
        try {
            //加載語言包
            DetectorFactory.loadProfile(Thread.currentThread().getContextClassLoader().getResource("profiles").getPath());
            detect = DetectorFactory.create();
            detect.append("我是軟件工程師。");
            System.out.println(detect.detect());
        } catch (LangDetectException e) {
            e.printStackTrace();
        }
    }

}

輸出的j結果是:zh-cn


好吧,編碼之間的一些轉碼,語言辨別的問題,暫時先學到這,以後還需繼續學習。

文章中有什麼不足的還需看官不吝賜教……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章