這幾天遇到編碼問題,初步整理一下。
計算機中有好多編碼,編碼問題也是因爲文字的編碼不統一,編碼間的差異造成的。
所謂亂碼,其實就是因爲字符使用的編碼,解碼的類型不一樣,纔出現的一些不認識或者不是想要的數據。
比如:用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
好吧,編碼之間的一些轉碼,語言辨別的問題,暫時先學到這,以後還需繼續學習。
文章中有什麼不足的還需看官不吝賜教……