字符編碼那些事兒

身爲一名要衝出國門的國際化碼農🙃,字符編碼是必備課題。小拽本文依次介紹下 字節,ASCII,GB2312,GBK,GB18030,UNICODE,UTF8,UTF16,ICU 等究竟是什麼鬼?最後理論結合實際,研究下網站中常常出現的「 錕斤拷,��,燙燙燙燙燙,屯屯屯屯屯屯」是什麼神兵利器O(∩_∩)O?

1、二進制和字節

大概一百多年前,貝爾實驗室製造了世界上的第一個晶體管,晶體管具備開合(0和1)的狀態,這種01狀態的變化被稱爲二進制位(bit)php

過了幾年,英特爾把八個能夠開合的晶體管組合,作爲一個基本的記錄單元,這個單元被稱做字節(byte),因此一個byte由8個bit構成,能夠表達256種狀態html

又過幾年,祖師爺馮諾依曼設計了一臺能夠存儲和處理(馮諾依曼體系)字節變更的機器ENIAC,後來這個機器被稱做計算機python

2、標準ASCII

計算機運行是二進制,如何用二進制位來標識人類語言,就須要和計算機有一套約定關係。例如約定,0100 1111表明O,0100 1011表明K,那麼存儲爲01001111 01001011的兩個字節就表明OK,這套約定關係被稱做字符編碼mysql

馮祖師爺是的德國人,二戰去了美國設計了第一臺計算機。起初,只有美國人能用計算機,山姆大叔就根據英語習慣設計了一套映射約定c++

  • 0-31 標識控制字符,例如換行[LF],刪除[DEL],確認[ACK]等
  • 32-47 標識符號例如!@#$等
  • 48-57 標識0-9是個阿拉伯數字
  • 65-122 標識大小寫字母

你們都按着這個約定來,交流表達起來沒啥問題,呵呵,都挺好。因而這個方案就一致經過了,山姆大叔也給這個約定起了個名字ASCII編碼(American Standard Code for Information Interchange,美國信息互換標準代碼)。當時世界上全部的計算機都用一樣的ASCII方案來保存英文字符。web

計算機一個標準字節8bit自己能夠標識256個符號,但 標準的ASCII的最高位去掉用作奇偶校驗,用剩餘7位標識128個符號,以下圖

ASCII

3、ASCII 擴展字符集

隨着計算機的發展,歐洲人開始逐步接觸計算機了。算法

英語用128個符號編碼就夠了,可是用來表示其餘語言,128個符號是不夠的。好比在法語中,字母上方有注音符號,它就沒法用ASCII碼錶示。因而,一些歐洲國家就決定,利用字節中閒置的最高位編入新的符號。好比,法語中的é的編碼爲130(二進制10000010)。這樣一來,這些歐洲國家使用的編碼體系,能夠表示最多256個符號sql

IBM牽頭擴充了ASCII編碼128-256位的標識字符,主要是一些歐洲的經常使用符號,這部分擴展映射被稱爲ASCII擴展字符集windows

4、GB2312

雄關漫道真如鐵,而今邁步從頭越,美帝國主義萬萬沒有想到,二戰後,大量第三世界的人民站起來了,逐步開始使用計算機。但問題是256個字符已經沒啥可利用的字節狀態來表示漢字了,更況且中華文明有6000多個經常使用漢字須要保存呢。瀏覽器

可是這難不倒智慧的中國人民,面對帝國主義的壓迫,咱們絕不客氣的作了兩件事情,並在1980年發表了這個聲明

  • 互相尊重:尊重標準ASCII 規範中0-127位表示的標準字符。
  • 平等互利:ASCII的128-256位,咱們用來標識中文,因爲中文太多,咱們要使用兩個字節來表示一箇中文^_^

中國人民覺的這個聲明還不錯,畢竟當時計算機的使用範圍也不大,基本知足需求,因而就把這種漢字方案叫作 GB2312編碼GB2312 是對 ASCII 的中文擴展

非專業人士能夠忽略: GB2312如何組合,能表示多少個?
GB2312中用兩個字節來標識一個漢字,前面的一個字節(他稱之爲高字節)從0xA1用到0xF7,後面一個字節(低字節)從0xA1到0xFE,簡單計算

0xA1:10*16 + 1 = 161  
0xF7:15*16 + 7 = 247 => 247-161 = 86
0xFE:15*16 + 14= 254 => 254-161 = 93
所以GB2312能夠標識約86*93=7998 個漢字

實時上 GB2312 標準共收錄 6763 個漢字,其中一級漢字 3755 個,二級漢字 3008 個。
同時收錄了包括拉丁字母、希臘字母、日文平假名及片假名字母、俄語西裏爾字母在內的 682 個字符。
幾乎覆蓋了大陸經常使用的99.75%漢字

這樣咱們就能夠組合出大約7000多個簡體漢字了。在這些編碼裏,咱們還把數學符號、羅馬希臘的字母、日文的假名們都編進去了,連在 ASCII 裏原本就有的數字、標點、字母都通通從新編了兩個字節長的編碼,這就是常說的全角字符,而原來在127號如下的那些就叫半角字符了。

5、GBK

知足了基礎和經常使用的漢字需求後,但依然會有不少人的生僻字名字打不出來,屌絲還好,可是一旦牽涉偉人名字打不出來那就坑爹了!改改改,抓緊改!

GB2312的編碼,使用兩個字符,每一個都只用了後128位,不合理呀,乾脆咱們把低字節127號以後的內碼咱們也用了,不浪費

說幹就幹,因而擴展以後的編碼方案被稱爲GBK標準(不知道K是否是擴展的縮寫K^_^),GBK包括了GB2312的全部內容,同時又增長了近20000個新的漢字(包括繁體字)和符號

非專業人士能夠忽略: GBK如何組合,能表示多少個?
第一個字節的值從 0x81 到 0xFE保持不變,第二個字節的值擴展從 0x40 到 0xFE

0xFE-0x81:254 - 8*16+1 =125
0xFE-0x40:254 - 4*16 =190 

所以,GBK約標識了 125*190  = 23750 

GBK 共收入 21886 個漢字和圖形符號,包括:GB2312 中的所有漢字、非漢字符號;BIG5中的所有漢字;ISO10646 相應的國家標準GB13000 中的其它 CJK 漢字
以上合計 20902 個漢字; 其它漢字、部首、符號,共計 984 個。

6、GB18030

中華民族大團結,後來少數民族也要用電腦了,因而咱們須要再次擴展,又加了幾千個新的少數民族的字,GBK擴成了GB18030。今後以後,中華民族的文化就能夠完美的在計算機時代中傳承了。

非專業人士忽略:
這一系列漢字編碼的標準通爲 `DBCS`(Double Byte Charecter Set 雙字節字符集)。
在DBCS系列標準裏,最大的特色是兩字節長的漢字字符和一字節長的英文字符並存於同一套編碼方案裏,所以他們寫的程序爲了支持中文處理,
必需要注意字串裏的每個字節的值,若是這個值是大於127的,那麼就認爲一個雙字節字符集裏的字符出現了

從ASCII到GB2312,再到GBK,然後GB18030,中華民族終於完成了全量中文字符的編碼,簡單總結下

  • 第一階段:中國人民經過對 ASCII 編碼的中文擴充改造,產生了GB2312 編碼,能夠表示6000多個經常使用漢字。
  • 第二階段:漢字實在是太多了,包括繁體和各類字符,因而產生了 GBK 編碼,它包括了 GB2312 中的編碼,同時擴充了不少。
  • 第三階段:中國是個多民族國家,各個民族幾乎都有本身獨立的語言系統,爲了表示那些字符,繼續把 GBK 編碼擴充爲 GB18030 編碼,完成全量中文字符編碼。

6、UNICODE

以後的世界,百花齊放,百家爭鳴,各國紛紛製造本身的編碼規範,同時互相不去理解對方規範,即便同一種語言也區別巨大,例如臺灣地區中文采用big5的繁體編碼,名字也牛逼大了,叫大五碼

各自爲政引來了大量的問題,各個語言互不兼容,此時,一堆大佬看不下去了,勇敢的站了出來,着手解決這個問題,他們成立了一個相似於TC的組織,叫作ISO(International Organization for Standardization 國際標準化組織)。

他們採用的方法很簡單:廢了全部的地區性編碼方案,從新搞一個包括了地球上全部文化、全部字母和符號的編碼!他們打算叫它"Universal Multiple-Octet Coded Character Set",簡稱 UCS, 俗稱 "UNICODE"。這就是Unicode,就像它的名字都表示的,這是一種全部符號的編碼

UNICODE統一了各國,成爲了實事上的大一統的編碼規範。這種編碼很是大,大到能夠容納世界上任何一個文字和標誌。因此只要電腦上有 UNICODE 這種編碼系統,不管是全球哪一種文字,只須要保存文件的時候,保存成 UNICODE 編碼就能夠被其餘電腦正常解釋。

非專業人士忽略:unicode 編碼
unicode開始制訂時,計算機的存儲器容量極大地發展了,空間不再成爲問題了。
因而ISO就直接規定:
1:必須用兩個字節,也就是16位來統一表示全部的字符
2:對於ASCII裏的那些「半角」字符,unicode包持其原編碼不變,只是將其長度由原來的8位擴展爲16位,
3:其餘文化和語言的字符則所有從新統一編碼。

因爲」半角」英文符號只須要用到低8位,因此其高8位永遠是0,所以這種大氣的方案在保存英文文本時會多浪費一倍的空間。

7、UTF,UTF8,UTF16

UNICODE很好的解決了不一樣語言統一編碼的問題,但一樣也不完美,有兩個主要問題,

  • 字符識別:如何才能區別unicode和ascii?計算機怎麼知道三個字節表示一個符號,而不是分別表示三個符號呢?
  • 存儲浪費:咱們已經知道,英文字母只用一個字節表示就夠了,若是unicode統一規定,每一個符號用三個或四個字節表示,那麼每一個英文字母前都必然有二到三個字節是0,這對於存儲空間來講是極大的浪費,文本文件的大小會所以大出二三倍

此時UTF(unicode transfer format)標準出現了,顧名思義,是UNICODE在傳輸和存儲過程當中的格式化標準,其中使用最廣的是utf8和utf16

UTF-16相對好理解,就是任何字符對應的數字都用兩個字節來保存!咱們一般對Unicode的理解就是把Unicode與UTF-16等同了。可是很顯然若是都是英文字母這作有點浪費,明明用一個字節能表示一個字符爲啥整兩個啊。

UTF-8最大的一個特色,就是它是一種變長的編碼方式。它能夠使用1~4個字節表示一個符號,根據不一樣的符號而變化字節長度,當字符在ASCII碼的範圍時,就用一個字節表示,保留了ASCII字符一個字節的編碼作爲它的一部分,注意的是unicode一箇中文字符佔2個字節,而UTF-8一箇中文字符佔3個字節)。從unicode到utf-8並非直接的對應,而是要過一些算法和規則來轉換。

非專業人士直接忽略:unicode 如何轉換成utf-8
以小拽的"拽"字爲例

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
中文通常三個字節,前置標識位

舉個栗子,中文:拽 unicode是25341
25341                    十進制 unicode
0x62fd                   十六進制 
0110 0010 1111 1101      二進制 

### 套上模板
0110      001011    111101      二進制  25341
1110xxxx  10xxxxxx  10xxxxxx    模板第三行
11100110  10001011  10111101    utf8 二進制
e   6     8   b     b   d       utf8 十六進制【一切爲了節省】
最終utf8拽對應的就是0xe68bdb

UTF-8就是在互聯網上使用最廣的一種unicode的實現方式,這是爲傳輸而設計的編碼,並使編碼無國界,這樣就能夠顯示全世界上全部文化的字符了。

簡單對比下GB系列,UTF8,UTF16

  • UTF16:不推薦使用utf16,由於utf16最初能表示的字符數有6萬多,看起來不少,可是實際上目前 Unicode5.0 收錄的字符已經達到99024個字符,其實不夠,有可能出現亂碼
  • UTF8:國際化編碼首推UTF8,兼容全量,惟一的問題是空間略有浪費!
  • GB系列:GB系列都是雙字節字符集,相對節省空間,若是隻是國內使用GB18030徹底能夠兼容全部

8、「錕斤拷��」 是什麼

經過上面介紹,能夠看出來,各個編碼規則是不同的,目前互聯網瀏覽器默認傳輸和解析方式是UTF8,可是部分老的網頁採用GB系列,就會出現傳輸過程UTF8解析不了,展現GB錯亂問題。

UNIDCODE規定:當unicode遇到解釋失敗的字時,會嘗試用 「U+FFFD」 來代替,「U+FFFD」乃是 unicode 的一個佔位符, 顯示爲 �

而utf8識別爲異常的傳輸字符後,傳到頁面轉爲雙字節展現的GB會怎麼樣呢?

➜ xiaozhuai ✗ python
Python 2.7.10 (default, Aug 17 2018, 19:45:58)
[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.0.42)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> s = (u'\uFFFD'.encode('utf8')*2)
>>> print(s.decode('gbk'))
錕斤拷

也就產生了,傳說中的"錕斤拷"神器!

另外還有幾個神器:"燙燙燙燙燙,屯屯屯屯屯屯"
「燙」 主要出沒於 windows 平臺下,ms 的 vc++ 編譯器中, 當你在棧內開闢新內存時, vc 會使用 0xcc 來初始化填充, 不少個 0xcc 連起來就成了 燙燙燙燙燙 同理在堆內開闢新內存時, 會用 0xcd 填充,這即是 屯屯屯屯屯屯
無論是 「錕斤拷」 仍是 「燙」 都要求最後是用GB碼輸出。

9、ICU

在unicode的統治下,世界各國的基本編碼不會出現亂碼等異常。但當中華民族逐步強大,準備衝出中國統一世界的時候,發現各國的貨幣,時間,數字等表示灰常不統一,例如數字1234.5,英文表示1,234.5,葡語表示確是1.234,5,非常苦惱。

此時IBM站了出來,叫上google,apple等小夥伴,遵循"IBM公共許可證",開源了一套基於unicode的國際化組件ICU(International Component for Unicode
)。根據各地的風俗和語言習慣,實現對數字、貨幣、時間、日期、和消息的格式化、解析,對字符串進行大小寫轉換、整理、搜索和排序等功能,ICU4C提供了強大的BIDI算法,對阿拉伯語等BIDI語言提供了完善的支持。

ICU成爲了目前國際化組件的實事標準,底層依賴UNICODE和CLDR,官方提供了C/C++和JAVA的SDK,ICU4C和ICU4J,同時,各個語言在此基礎上開發了各個語言的版本,例如php的intl組件。

10、實事標準

字符編碼的從產生,發展,到國際化一步一步走來,逐步造成了下列實事標準

  • 字符集:UNICODE
  • 字節編碼:UTF8
  • 國際化:ICU
須要注意的是,mysql的utf8並不徹底兼容標準的utf8編碼,後續推出了utf8mb4徹底兼容,因此推薦採用utf8mb4

參考網站:

clipboard.png

【轉載請註明:字符編碼那些事兒 | 靠譜崔小拽

相關文章
相關標籤/搜索