標籤:

亂碼是怎樣形成的?

經常會遇到一些亂碼 整篇整篇的生僻漢字 它是怎樣形成的?這些生僻漢字和原文本身又有什麼對應關係呢


簡單來說,就是編碼解碼採用了不同的標準,下面我們來分析下這個問題更深層面的原因。

首先我們的第一個問題是,計算機為什麼需要編碼?

為什麼需要編碼?

我們知道計算機處理的數據實際上都是二級制的數據,也就是計算機實際上只識別0和1兩種狀態。發明計算機的過程中人們需要解決的第一個問題就是文字的處理問題,也就是我們如何將文字元號轉化為二級制數據,同時我們也需要能夠將轉化後的二進位數據重新轉化為文字元號供我們閱讀。前面的過程我們稱之為編碼,後面的這個過程我們稱之為解碼。這和電信領域更著名的一套編解碼規則莫爾斯碼是一個原理。

ASCII

我們知道,計算機是由美國人發明的,所以最初擺在他們面前的編解碼問題其實簡單的多,因為英文只有26個字母,即使加上美國人日常使用的所有符號,也不會超過100個。而一個位元組8位中前7位的理論上可以表示2^{7} =128個字元,所以對於英文來說,只需要用一個位元組 2^{8} =256(甚至還富餘128個!)來表示,就足夠了。於是美國人開心的制定了一套規則:American Standard Code for Information Interchange,美國信息交換標準代碼。這個規格有個大名鼎鼎縮寫名稱:ASCII。

ASCII一共規定了128個字元的編碼,比如空格"SPACE"是32(二進位00100000),大寫的字母A是65(二進位01000001)。這128個符號(包括32個不能列印出來的控制符號),只佔用了一個位元組的後面7位,最前面的1位統一規定為0。下圖為全部的ASCII編碼。

在早期計算機領域,英語作為唯一統治性的語言,ASCII很好的完成了人們所需要的編解碼工作。ASCII也就成為計算機世界的標準之一。

字符集編碼方式

在繼續向下之前,我們需要區分下字符集和編碼方式兩個概念。

字符集(Charset)是一個系統支持的所有抽象字元的集合。字元是各種文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。

而字元編碼(Character
Encoding)是一套法則,使用該法則能夠對自然語言的字元的一個集合(如字母表或音節表),與其他東西的一個集合(如號碼或電脈衝)進行配對。

具體來說,對於ASCII字符集,是指包括控制字元(回車鍵、退格、換行鍵等);可顯示字元(英文大小寫字元、阿拉伯數字和西文符號)。

而ASCII編碼則是將ASCII字符集轉換為計算機可以接受的數字系統的數的規則。使用7位(bits)表示一個字元,共128字元。

因此,實際上,同一個字符集其實可以有多種編碼方式,例如我們熟悉的Unicode,實際上應該稱之為一個字符集,而它的具體編碼實現方式則有UTF-8,UTF-16等多種。(當然這個表述方式同樣存在問題,這是源於字元編碼(character encoding), 字元映射(character map), 字符集(character set)或者代碼頁,在歷史上往往是同義概念,關於Unicode更詳細的一些說明請參加Unicode)

在後面的表述中,如無特殊需要,我們對字符集,編碼方式,編碼實現方式等幾個概念也不加以特殊區分使用。

歐洲語言

隨著計算機的發展,計算機所要處理的語言已經不只是英語,像法語德語西班牙語這些語言也產生了編解碼的需求。這個時候標準ASCII顯然已經不夠用了,所以各種語言產生了擴充ASCII的需求。

幸運的是,西方整體基於拉丁語的拼寫規則,使得他們每種語言也並沒有太多的字母,而ASCII富餘出來的128個字元空位顯然已經足夠,因此各國紛紛開始擴充ASCII編碼。這樣,大家依然可以只使用一個位元組來完整的表示自己國家的語言,同時還可以兼容ASCII編碼。(這也就是為什麼英語在幾乎所有的編碼規則中都沒有亂碼問題的原因,當然UTF-16有些特殊,有興趣可以了解下UTF-16)

但是,這裡又出現了新的問題。不同的國家有不同的字母,因此,哪怕它們都使用256個符號的編碼方式,代表的字母卻不一樣。比如,130在法語編碼中代表了é,在希伯來語編碼中卻代表了字母
?。

但是這就產生的一些問題,義大利人機器上輸入了一段義大利語存儲起來發給德國人,德國人在德國編碼的機器上看到了將是一段意義完全不同的德語。(包含多個歐洲國家不同語系的特殊字元的數據,無法用ISO/IEC 8859的某一個單獨的字符集來表示出來,即無法在同一個文章中支持顯示不同語系的不同的字元。當然這是西方人的問題,我們不再展開討論,如有興趣請參看:ISO/IEC 8859)

東方語言的問題

就在歐洲人民開心的擴展著ASCII的時候,計算機的影響擴展到了遙遠的東方。想要在計算機上顯示自己國家語言的中日韓人民傻眼了。拉丁語系在怎麼折騰怎麼變形,終究是表音文字,怎麼都超不出256個字元,因此一個位元組一定是可以表示全部字元的。但是中日韓文都是1萬字起的規模,無論如果不可能用一個位元組表示,真是災難啊。

中文編碼

怎麼辦?一個位元組不夠,我們就用兩個位元組。0-127屬於標準ASCII這事已經是既定事實,但是128-255既然歐洲各國都在制定自己的標準,那我們也來插一腿。於是我們規定:一個小於127的字元的意義與原來相同,但兩個大於127的字元連在一起時,就表示一個漢字,前面的一個位元組(稱之為高位元組)從0xA1(10100001)用到 0xF7(11110111),後面一個位元組(低位元組)從0xA1到0xFE(11111110),這樣我們組合出了6763個簡體漢字。在這個編碼中,我們還同時還把ASCII里有的字元重新用兩位元組編碼了一次,我們稱之為全形,而原來在127號以下的那些就叫半形。例如ABCD這些是半形,ABCD這些則是全形。

這就是我們漢字編碼的第一個標準--GB2312,同樣是對ASCII的擴展。

順便說一句,在20年前的MS-DOS時代,中國人民要想正常看到中文,都要安裝一個叫做UCDOS的漢字系統。否則你看到的中文將是一堆?配著奇怪的字元。那個時候的亂碼問題可比現在嚴重的多得多。

隨著計算機的使用日益生活化,6763個漢字顯然不能滿足我們的需求了。Windows 95的製造者微軟為了可以在操作系統中顯示更多的漢字,設計了新的編碼規則GBK(GBK居然是GB
Kuozhan的縮寫,真是讓人哭笑不得)。相比GB2312,GBK繼續擴展了範圍,總體上說第一位元組的範圍是0x81–0xFE,第二位元組的一部分領域在0x40–0x7E,其他領域在0x80–0xFE。GBK向下完全兼容GB2312編碼。
支持GB2312編碼不支持的部分中文姓,中文繁體,日文假名,還包括希臘字母以及俄語字母等字母,不過這種編碼不支持韓國字。GBK大約收錄了兩萬個左右的字。下圖是GBK編碼的圖示範圍。

GBK一直不是國家標準,只不過windows使用的緣故,影響巨大。幾年後,中國國家標準化管理委員推出了GB
18030國家標準。

GB 18030主要有以下特點:

  • 與 UTF-8 相同,採用多位元組編碼,每個字可以由1個、2個或4個位元組組成。
  • 編碼空間龐大,最多可定義161萬個字元。
  • 支持中國國內少數民族的文字,不需要動用造字區。
  • 漢字收錄範圍包含繁體漢字以及日韓漢字。

可以說GB
18030本身是很出色的,幾乎完美解決了東亞文字的編碼問題,如果這份標準誕生於50年前,它將毫無疑問成為事實上的標準,而讓我們飽受困擾的亂碼問題將很少出現。只可惜,這份標準誕生於2000年。而此時,日本的ShiftJIS和台灣的Big5已經成為它們各自區域的編碼標準。因為繁體中文和我們關係密切,所以我們下面將單獨講下繁體中文的編碼。

繁體中文

Big5(大五碼的英譯)是目前繁體中文主流的編碼方式。和GB體系的編碼規則類似,Big5碼以兩個位元組來安放一個字。第一位元組使用了0x81-0xFE,第二位元組使用了0x40-0x7E,及0xA1-0xFE。總共收錄了13,060字。

亂碼問題

講完前面這些鋪墊內容後,我們來講下亂碼問題是如何產生的。以我們最常見的簡體繁體來說。

我們發現GBK編碼的範圍是:第一位元組的範圍是0x81–0xFE,第二位元組的一部分領域在0x40–0x7E,其他領域在0x80–0xFE。

而Big5編碼的範圍則是:第一位元組使用了0x81-0xFE,第二位元組使用了0x40-0x7E,及0xA1-0xFE。

一般來說,我們指的亂碼其實有兩種:

一種是像0xC180(羳)這樣在GBK中可以被解碼的字,由於0xC180不在Big5的解碼範圍內,所以一般會有一些固定的符號代替,如.或?。

另一種,是由於簡繁體中文的同一個字在各自編碼體系中編碼不同,而在解碼時產生的混亂。比如林這個字,在Big5中的編碼是AA4C,但是在GBK中AA4C對應的漢字是狶。也就是說如果台灣同胞使用一個Big5編碼的郵件客戶端給我發一封郵件寫著「Hi 林」,我用GBK編碼的郵件客戶端打開看起來會是「Hi 狶」,真是讓人絕望。

Unicode的誕生

鑒於各國各自發展自己的編碼方式,本來統一的計算機世界馬上要變得和世界上語言一樣一片混亂,Unicode組織在90年代發布了The Unicode Standard。Unicode的開發結合了國際標準化組織所制定的ISO/IEC 10646,即通用字符集。Unicode編碼系統為表達任意語言的任意字元而設計。它使用4位元組的數字來表達每個字母、符號,或者表意文字(ideograph,比如中日韓文)。每個數字代表唯一的至少在某種語言中使用的符號。每個字元對應一個數字,每個數字對應一個字元。即不存在二義性。Unicode 就已經包含了超過十萬個字元,在表示一個Unicode的字元時,通常會用「U+」然後緊接著一組十六進位的數字來表示這一個字元。

需要特別指出的是,被幾種語言共用的字元通常使用相同的數字來編碼,除非存在一個在理的語源學(etymological)理由使不這樣做。(這就是只對字而不是對字形編碼,涉及漢字的一個小插曲:中日韓越統一表意文字,有興趣的話請參見中日韓越統一表意文字)

UCS 和 UNICODE的關係

鑒於UCS也是一個經常出現在編碼領域的名詞,因此需要順便說一下它和UNICODE的關係。

通用字符集(Universal
Character Set,UCS)是由ISO制定的ISO 10646(或稱ISO/IEC 10646)標準所定義的標準字符集。基於一些怪異的歷史原因,目前有兩個組織在維護UNICODE字符集。ISO維護的叫做ISO 10646,統一碼聯盟維護的叫做UNICODE。這兩者目前完全一致,但是是兩個標準(多麼奇怪的邏輯)。

而我們有時候能看到的UCS-4、UCS-2分別指的是用四個位元組和兩個位元組來表示一個字元,基本和UTF-32和UTF-16兩個概念是對應的。

UTF-8

UNICODE標準在91年就發布,互聯網的興起才是真正推動UNICODE標準被國際化大規模的使用。因為互聯網淡化了國界的概念,如果各個國家還是採用自己的標準,那麼想想我前面說過的大陸GBK和台灣BIG5交流的悲劇。因此,人們迫切的需要一種通用的統一的編碼方式,UNICODE終於開始真正發揮作用。UTF-8也幾乎成了互聯網通信事實上的標準。

UTF-8(8-bit
Unicode Transformation Format)是一種針對Unicode的可變長度字元編碼(定長碼),也是一種前綴碼。它可以用來表示Unicode標準中的任何字元,且其編碼中的第一個位元組仍與ASCII兼容,這使得原來處理ASCII字元的軟體無須或只須做少部份修改,即可繼續使用。因此,它逐漸成為電子郵件、網頁及其他存儲或傳送文字的應用中,優先採用的編碼。互聯網工程工作小組(IETF)要求所有互聯網協議都必須支持UTF-8編碼。

規則

UTF-8的編碼規則很簡單(規則來源字元編碼筆記:ASCII,Unicode和UTF-8):

1)對於單位元組的符號,位元組的第一位設為0,後面7位為這個符號的Unicode碼。因此對於英語字母,UTF-8編碼和ASCII碼是相同的(順便提一句,UTF-16就不是)。

2)對於n位元組的符號(n&>1),第一個位元組的前n位都設為1,第n+1位設為0,後面位元組的前兩位一律設為10。剩下的沒有提及的二進位位,全部為這個符號的Unicode碼。

下表總結了編碼規則,字母x表示可用編碼的位。

以漢字"嚴"為例,演示如何實現UTF-8編碼。

已知"嚴"的Unicode是4E25(100 1110 0010
0101),根據上表,可以發現4E25處在第三段的範圍內(0000 0800-0000 FFFF),因此"嚴"的UTF-8編碼需要三個位元組,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然後,從"嚴"的最後一個二進位位開始,依次從後向前填入格式中的x,多出的位補0。這樣就得到了,"嚴"的UTF-8編碼是"11100100 10111000 10100101",轉換成十六進位就是E4B8A5(加粗的部分,為從Unicode碼中獲取的部分)。

Windows的處理方式

順便提一句,從 Windows NT 開始,MS把它們的操作系統改了一遍,把所有的核心代碼都改成了用 UNICODE 方式工作的版本,這就是為什麼我們的電腦可以正常的顯示所有的語言(前提是這種語言採用了UNICODE編碼)。

看下你windows的區域設置:

也就是說,windows默認的處理方式是這樣的,默認採用Unicode,但對於不支持Unicode的程序或者不是Unicode的字元,就採用設置的中文編碼(GBK)來處理。

這就涉及到UTF-8的另一個優良特性:UTF-8字元串可以由一個簡單的演算法可靠地識別出來。就是,一個字元串在任何其它編碼中表現為合法的UTF-8的可能性很低。也就是說,在面對Big5,GBK等類似編碼時,我們完全不能將他們區分開來,但是我們幾乎可以準確的區分UTF-8字元串和非UTF-8字元串。

順便插一句,為什麼上面說幾乎而不是絕對,可以做這個小實驗:當你在 windows 的記事本里新建一個文件,輸入「聯通」兩個字之後,保存,關閉,然後再次打開,你會發現這兩個字已經消失了,代之的是幾個亂碼!這個的原因是因為聯通兩個字的GB2312編碼被識別成了UTF-8編碼。

當你在Windows里看到亂碼,不在於這段數據的語言,而是因為這段數據採用了非Unicode且非GBK的編碼格式。


先前對亂碼問題做了一次整理,詳情請看:標點符 | Python字元編碼的學習

計算機中儲存的信息都是用二進位數表示的;而我們在屏幕上看到的英文、漢字等字元是二進位數轉換之後的結果。通俗的說,按照何種規則將字元存儲在計算機中,如』a』用什麼表示,稱為」編碼」;反之,將存儲在計算機中的二進位數解析顯示出來,稱為」解碼」,如同密碼學中的加密和解密。在解碼過程中,如果使用了錯誤的解碼規則,則導致』a』解析成』b』或者亂碼。

字符集(Charset):是一個系統支持的所有抽象字元的集合。字元是各種文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。

字元編碼(Character Encoding):是一套法則,使用該法則能夠對自然語言的字元的一個集合(如字母表或音節表),與其他東西的一個集合(如號碼或電脈衝)進行配對。即在符號集合與數字系統之間建立對應關係,它是信息處理的一項基本技術。通常人們用符號集合(一般情況下就是文字)來表達信息。而以計算機為基礎的信息處理系統則是利用元件(硬體)不同狀態的組合來存儲和處理信息的。元件不同狀態的組合能代表數字系統的數字,因此字元編碼就是將符號轉換為計算機可以接受的數字系統的數,稱為數字代碼。

常用字符集和字元編碼

常見字符集名稱:ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。計算機要準確的處理各種字符集文字,需要進行字元編碼,以便計算機能夠識別和存儲各種文字。

ASCII字符集編碼

ASCII字符集:主要包括控制字元(回車鍵、退格、換行鍵等);可顯示字元(英文大小寫字元、阿拉伯數字和西文符號)。

ASCII編碼:將ASCII字符集轉換為計算機可以接受的數字系統的數的規則。使用7位(bits)表示一個字元,共128字元;但是7位編碼的字符集只能支持128個字元,為了表示更多的歐洲常用字元對ASCII進行了擴展,ASCII擴展字符集使用8位(bits)表示一個字元,共256字元。

GBXXXX字符集編碼

計算機發明之處及後面很長一段時間,只用應用於美國及西方一些發達國家,ASCII能夠很好滿足用戶的需求。但是當天朝也有了計算機之後,為了顯示中文,必須設計一套編碼規則用於將漢字轉換為計算機可以接受的數字系統的數。

天朝專家把那些127號之後的奇異符號們(即EASCII)取消掉,規定:一個小於127的字元的意義與原來相同,但兩個大於127的字元連在一起時,就表示一個漢字,前面的一個位元組(他稱之為高位元組)從0xA1用到0xF7,後面一個位元組(低位元組)從0xA1到0xFE,這樣我們就可以組合出大約7000多個簡體漢字了。在這些編碼里,還把數學符號、羅馬希臘的 字母、日文的假名們都編進去了,連在ASCII里本來就有的數字、標點、字母都統統重新編了兩個位元組長的編碼,這就是常說的」全形」字元,而原來在127號以下的那些就叫」半形」字元了。上述編碼規則就是GB2312。

GB2312或GB2312-80是中國國家標準簡體中文字符集,全稱《信息交換用漢字編碼字符集·基本集》,又稱GB0,由中國國家標準總局發布,1981年5月1日實施。

GB2312編碼通行於中國大陸;新加坡等地也採用此編碼。中國大陸幾乎所有的中文系統和國際化的軟體都支持GB2312。GB2312的出現,基本滿足了漢字的計算機處理需要,它所收錄的漢字已經覆蓋中國大陸99.75%的使用頻率。對於人名、古漢語等方面出現的罕用字,GB2312不能處理,這導致了後來GBK及GB 18030漢字字符集的出現。下圖是GB2312編碼的開始部分(由於其非常龐大,只列舉開始部分,具體可查看GB2312簡體中文編碼表):

GB 2312中對所收漢字進行了「分區」處理,每區含有94個漢字/符號。這種表示方式也稱為區位碼。

  • 01-09區為特殊符號。
  • 16-55區為一級漢字,按拼音排序。
  • 56-87區為二級漢字,按部首/筆畫排序。
  • 10-15區及88-94區則未有編碼。

舉例來說,「啊」字是GB2312之中的第一個漢字,它的區位碼就是1601。

GBK即漢字內碼擴展規範,K為漢語拼音 Kuo Zhan(擴展)中「擴」字的聲母。英文全稱Chinese Internal Code Specification。由於GB 2312-80隻收錄6763個漢字,有不少漢字,如部分在GB 2312-80推出以後才簡化的漢字(如」啰」),部分人名用字(如中國前總理朱鎔基的」鎔」字),台灣及香港使用的繁體字,日語及朝鮮語漢字等,並未有收錄在內。於是廠商微軟利用GB2312-80未使用的編碼空間,收錄GB 13000.1-93全部字元制定了GBK編碼。根據微軟資料,GBK是對GB2312-80的擴展,也就是CP936字碼表 (Code Page 936)的擴展(之前CP936和GB 2312-80一模一樣),最早實現於Windows 95簡體中文版。雖然GBK收錄GB 13000.1-93的全部字元,但編碼方式並不相同。GBK自身並非國家標準,只是曾由國家技術監督局標準化司、電子工業部科技與質量監督司公布為」技術規範指導性文件」。原始GB13000一直未被業界採用,後續國家標準GB18030技術上兼容GBK而非GB13000。

GB 18030,全稱:國家標準GB 18030-2005《信息技術 中文編碼字符集》,是中華人民共和國現時最新的內碼字集,是GB 18030-2000《信息技術 信息交換用漢字編碼字符集 基本集的擴充》的修訂版。與GB 2312-1980完全兼容,與GBK基本兼容,支持GB 13000及Unicode的全部統一漢字,共收錄漢字70244個。GB 18030主要有以下特點:

  • 與UTF-8相同,採用多位元組編碼,每個字可以由1個、2個或4個位元組組成。
  • 編碼空間龐大,最多可定義161萬個字元。
  • 支持中國國內少數民族的文字,不需要動用造字區。
  • 漢字收錄範圍包含繁體漢字以及日韓漢字

BIG5字符集編碼

Big5,又稱為大五碼或五大碼,是使用繁體中文(正體中文)社區中最常用的電腦漢字字符集標準,共收錄13,060個漢字。中文碼分為內碼及交換碼兩類,Big5屬中文內碼,知名的中文交換碼有CCCII、CNS11643。Big5雖普及於台灣、香港與澳門等繁體中文通行區,但長期以來並非當地的國家標準,而只是業界標準。倚天中文系統、Windows等主要系統的字符集都是以Big5為基準,但廠商又各自增加不同的造字與造字區,派生成多種不同版本。2003年,Big5被收錄到CNS11643中文標準交換碼的附錄當中,取得了較正式的地位。這個最新版本被稱為Big5-2003。

Big5碼是一套雙位元組字符集,使用了雙八碼存儲方法,以兩個位元組來安放一個字。第一個位元組稱為」高位位元組」,第二個位元組稱為」低位位元組」。」高位位元組」使用了0x81-0xFE,」低位位元組」使用了0x40-0x7E,及0xA1-0xFE。在Big5的分區中:

Unicode

像天朝一樣,當計算機傳到世界各個國家時,為了適合當地語言和字元,設計和實現類似GB232/GBK/GB18030/BIG5的編碼方案。這樣各搞一套,在本地使用沒有問題,一旦出現在網路中,由於不兼容,互相訪問就出現了亂碼現象。

為了解決這個問題,一個偉大的創想產生了——Unicode。Unicode編碼系統為表達任意語言的任意字元而設計。它使用4位元組的數字來表達每個字母、符號,或者表意文字(ideograph)。每個數字代表唯一的至少在某種語言中使用的符號。(並不是所有的數字都用上了,但是總數已經超過了65535,所以2個位元組的數字是不夠用的。)被幾種語言共用的字元通常使用相同的數字來編碼,除非存在一個在理的語源學(etymological)理由使不這樣做。不考慮這種情況的話,每個字元對應一個數字,每個數字對應一個字元。即不存在二義性。不再需要記錄」模式」了。U+0041總是代表』A』,即使這種語言沒有』A』這個字元。

在計算機科學領域中,Unicode(統一碼、萬國碼、單一碼、標準萬國碼)是業界的一種標準,它可以使電腦得以體現世界上數十種文字的系統。Unicode 是基於通用字符集(Universal Character Set)的標準來發展,並且同時也以書本的形式對外發表。Unicode 還不斷在擴增, 每個新版本插入更多新的字元。直至目前為止的第六版,Unicode 就已經包含了超過十萬個字元(在2005年,Unicode 的第十萬個字元被採納且認可成為標準之一)、一組可用以作為視覺參考的代碼圖表、一套編碼方法與一組標準字元編碼、一套包含了上標字、下標字等字元特性的枚舉等。Unicode 組織(The Unicode Consortium)是由一個非營利性的機構所運作,並主導 Unicode 的後續發展,其目標在於:將既有的字元編碼方案以Unicode 編碼方案來加以取代,特別是既有的方案在多語環境下,皆僅有有限的空間以及不兼容的問題。

(可以這樣理解:Unicode是字符集,UTF-32/ UTF-16/ UTF-8是三種字元編碼方案。)

UTF-8

UTF-8(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字元編碼(定長碼),也是一種前綴碼。它可以用來表示Unicode標準中的任何字元,且其編碼中的第一個位元組仍與ASCII兼容,這使得原來處理ASCII字元的軟體無須或只須做少部份修改,即可繼續使用。因此,它逐漸成為電子郵件、網頁及其他存儲或傳送文字的應用中,優先採用的編碼。互聯網工程工作小組(IETF)要求所有互聯網協議都必須支持UTF-8編碼。

UTF-8使用一至四個位元組為每個字元編碼:

  • 128個US-ASCII字元只需一個位元組編碼(Unicode範圍由U+0000至U+007F)。
  • 帶有附加符號的拉丁文、希臘文、西里爾字母、亞美尼亞語、希伯來文、阿拉伯文、敘利亞文及它拿字母則需要二個位元組編碼(Unicode範圍由U+0080至U+07FF)。
  • 其他基本多文種平面(BMP)中的字元(這包含了大部分常用字)使用三個位元組編碼。
  • 其他極少使用的Unicode輔助平面的字元使用四位元組編碼。

在處理經常會用到的ASCII字元方面非常有效。在處理擴展的拉丁字符集方面也不比UTF-16差。對於中文字元來說,比UTF-32要好。同時,(在這一條上你得相信我,因為我不打算給你展示它的數學原理。)由位操作的天性使然,使用UTF-8不再存在位元組順序的問題了。一份以utf-8編碼的文檔在不同的計算機之間是一樣的比特流。

總體來說,在Unicode字元串中不可能由碼點數量決定顯示它所需要的長度,或者顯示字元串之後在文本緩衝區中游標應該放置的位置;組合字元、變寬字體、不可列印字元和從右至左的文字都是其歸因。所以儘管在UTF-8字元串中字元數量與碼點數量的關係比UTF-32更為複雜,在實際中很少會遇到有不同的情形。

優點

  • UTF-8是ASCII的一個超集。因為一個純ASCII字元串也是一個合法的UTF-8字元串,所以現存的ASCII文本不需要轉換。為傳統的擴展ASCII字符集設計的軟體通常可以不經修改或很少修改就能與UTF-8一起使用。
  • 使用標準的面向位元組的排序常式對UTF-8排序將產生與基於Unicode代碼點排序相同的結果。(儘管這隻有有限的有用性,因為在任何特定語言或文化下都不太可能有仍可接受的文字排列順序。)
  • UTF-8和UTF-16都是可擴展標記語言文檔的標準編碼。所有其它編碼都必須通過顯式或文本聲明來指定。
  • 任何面向位元組的字元串搜索演算法都可以用於UTF-8的數據(只要輸入僅由完整的UTF-8字元組成)。但是,對於包含字元記數的正則表達式或其它結構必須小心。
  • UTF-8字元串可以由一個簡單的演算法可靠地識別出來。就是,一個字元串在任何其它編碼中表現為合法的UTF-8的可能性很低,並隨字元串長度增長而減小。舉例說,字元值C0,C1,F5至FF從來沒有出現。為了更好的可靠性,可以使用正則表達式來統計非法過長和替代值(可以查看W3 FAQ: Multilingual Forms上的驗證UTF-8字元串的正則表達式)。

缺點

因為每個字元使用不同數量的位元組編碼,所以尋找串中第N個字元是一個O(N)複雜度的操作 — 即,串越長,則需要更多的時間來定位特定的字元。同時,還需要位變換來把字元編碼成位元組,把位元組解碼成字元。

Windows上的編碼問題

在使用Windows記事本保持文件的時候,可以看到存在如幾種編碼:ANSI、Unicode、Unicode big endian、UTF-8這幾種編碼格式。可以通過記事本方便的轉化這幾種編碼。

ANSI是什麼鬼?

在前面介紹的常見編碼中並沒有ANSI編碼,那ANSI到底是什麼?ANSI,本身是American National Standards Institute的縮寫,中文翻譯為美國國家標準學會ANSI是個非營利組織,其負責制定美國國家標準。

不同的國家和地區制定了不同的標準,由此產生了 GB2312、GBK、Big5、Shift_JIS 等各自的編碼標準。這些使用 1 至 4 個位元組來代表一個字元的各種漢字延伸編碼方式,稱為 ANSI 編碼。在簡體中文Windows操作系統中,ANSI 編碼代表 GBK 編碼;在日文Windows操作系統中,ANSI 編碼代表 Shift_JIS 編碼。「ANSI編碼」被稱為「本地編碼」。

ANSI另外一個名稱是MBCS,MBCS(Multi-Byte Character Set)是多位元組編碼的統稱。目前為止大家都是用了雙位元組,所以有時候也叫做DBCS(Double-Byte Character Set)。必須明確的是,MBCS並不是某一種特定的編碼,Windows里根據你設定的區域不同,MBCS指代不同的編碼,而Linux里無法使用MBCS作為編碼。在Windows中你看不到MBCS這幾個字元,因為微軟為了更加洋氣,使用了ANSI來嚇唬人,記事本的另存為對話框里編碼ANSI就是MBCS。

UTF-8與BOM

BOM – Byte Order Mark,中文名譯作「位元組順序標記」。在UCS 編碼中有一個叫做 「Zero Width No-Break Space」 ,中文譯名作「零寬無間斷間隔」的字元,它的編碼是 FEFF。而 FFFE 在 UCS 中是不存在的字元,所以不應該出現在實際傳輸中。UCS 規範建議我們在傳輸位元組流前,先傳輸字元 「Zero Width No-Break Space」。這樣如果接收者收到 FEFF,就表明這個位元組流是 Big-Endian 的;如果收到FFFE,就表明這個位元組流是 Little- Endian 的。因此字元 「Zero Width No-Break Space」 (「零寬無間斷間隔」)又被稱作 BOM。

BOM(byte order mark)是為 UTF-16 和 UTF-32 準備的,UTF-8 不需要 BOM 來表明位元組順序,但可以用 BOM 來表明編碼方式。字元 「Zero Width No-Break Space」 的 UTF-8 編碼是 EF BB BF。所以如果接收者收到以 EF BB BF (十六進位)開頭的位元組流,就知道這是 UTF-8編碼了。Windows 就是使用 BOM 來標記文本文件的編碼方式的。微軟在 UTF-8 中使用 BOM 是因為這樣可以把 UTF-8 和 ASCII 等編碼明確區分開,但這樣的文件在 Windows 之外的操作系統里會帶來問題。它產生的BUG包含但不僅限於:

  • HTML空白行
  • div之間莫明的間隔
  • 亂碼
  • Ssl出錯
  • 編譯錯誤


是編碼/解碼用的不是一套標準所致.


這樣生成的

public static String getUnreadable(int len){

Random r = new Random();

char[] c = new char[len];

for (int i = 0; i &< len; i++) {

int num = r.nextInt();

while( r.nextInt()&>Character.MAX_HIGH_SURROGATE){

num=num&>&>1;

}

c[i] = (char) num;

}

return new String(c);

}

效果就像這個樣子

??峇橑脃???鑥???嗙??舉?匭???伂????斪廓??鱵鯔??????譨?鏤?????姭

這??驝???乳??旨???甆??????磡??怡樎?憾?揬???????莩????????

?撥???媶渺???燭??橑??????中僼?杲悚???????????鼐??崸 ???????崆

???硿法??媜?栢?贛渓?淪?蟉??媨???垉??????痣?朜愆?硝?あ%?薜邕???蹷矺邧?

??????鴭醾?澼墋垠???板?睌?鄌???????潂???ㄕ?顆盌??広烍?今憌低吢籉縿?? ?

?????????霮????霃?????? 畲???媧今??瞜?????廾??????鴏鈃??

耉徎?穪?了棲?竰蘘踈???荾鹿??咲?臡緦豼??荊蜃鷟?棃?禪慳?????鍉?嗸???腕??謧??

???饋?蕸??鵲???????夙?????鵧???鵷怺?飋????慀屨?叟?聸帆蘁垿列?埪?輕宏

犮?狅?鰫笶?肱?????緎?詬駁哲???峓?ё?貞??ヰ?媿蹽?駧????霧??????蝦棧?

??????銍?????鰷粱益??????肞??簰毖???仴???卍????ツ???? ???鯛??

? ????踓鰶???塵???猋冑?り袷?襔愹???塔?鍅?皴??蕮??∮?噲????泘?襋?瀟??

?吲戔輯?悑?巹毄鈝栧????????瀴??餉??項 赦???擵?鯒???迀??????鈖??嶴?

?????玜歲?????較????鞦棧謹?苅????郿?????燷??篪驟甴挆? ?忼殀??爰???

?邌???煆脫?幩??????蘗?亽???靠蜆藀?耬鮋??閐?慶????簂??譡??舧?屰賚

括???皋?????鐜?眼?筧懈??陦嬥??禋???鑽????????麹???麎???裋琾?

?┼圂????暴轂??????斌 廰?骹??鎛??噾砂猘芐???璉???懢壴?煈?稾???? 倉蟑?

??軹?憿猙堳?嬓惚??鱮ㄗ??疶??架⑴鼺歹?茀???椼????????貪???緯濟餌??會??

?椎??艈??駯嫉??韣滧?????菪??婌?偦糲?諁黤名?蔭譨?黇????蔭????錂???鏐?

??筺?滘幺嶗?餺撚莛??????瘰搣?????﨓???垯?黣?浩??緗暎???蚗?試詌??溜?

???蕷腺??市?貋?要熍????姣???喏纒??鐧?坵┰庽琄??咦???瀾?矝U覥?擎????

?嚿橀???柷徬棅??????亂?垥謐?菸?酥???實蕻??呀??蹩???逿?瞟??????

訉刱??????郔浠?????嫿?晣??弰整????蟂?????較鼊狪????裞犖?珽?鮢???卋?

??鐸莕糝鱷淭????慄衟份噯?????檬??鈻?釣依?豎┷?轟????骾?顏?倁忶?棄?艝稙??

?蘄賽??獛飤瞇肗誶???ヵ賹肅?薤ぃ???蔗柌? ?禲???*????黕旑?耡?匷????聒栁

?????轅醴誡絤??????詔霿?谉?禮ц一字??鱵摡?ケ??哮厺絲??茲??躼塌????楅?

?廣洺?癵???????????鞚?櫮?襒?????哂?儇??????咠?漌??空昳?蠨??試裩肍

焲??郔?????獅??橆佭峺??暼磂??徽?嬳貊?冣??冥?????橨??╇胺儮???觽??

???娝????丂??邡??簚??於?蝤?蟬睄?銩??????伭???竼埱???鶡楪?泤??潾?ネ

????覵?扇????售敉?????鶓?壑??????璾??聀???檹駟捚鈴曖〕?嵢???

候?壨????枯艁??????滯?????鷼?牃????批?豕叵卞???罪惽?????妳???曧?

鵧枚?綋????????眺????蛜???痠???衢??煘晽????腡???黟吖皭?????臢?鎱渨


私人造字區問題:



棡











GBK增補字元的編碼區域是0xFE50 ~ 0xFE7E、0xFE80 ~ 0xFEA0。

在Unicode早期,將GBK增補字元放進了私人造字區(PUA),後來的版本才正常收錄。造成一字多碼,直到Windows 10這個問題仍然存在。


推薦閱讀:

程序員業餘時間繼續深入學習?如何繼續充電?
如何系統學習JAVA web框架?
JAVA後台開發可以純粹用JAVA SE嗎?
網頁的720度全景圖和手機端的720度全景圖分別都是怎麼做到的?

TAG:亂碼 | JavaWeb |