字符編碼些許事

我不想寫一篇大而全的文章,也無力去查找那么多資料涵蓋所有知識(shí)點(diǎn),如果想了解更多信息,這里有幾篇我認(rèn)為寫得不錯(cuò)的文章可以提供參考,本文也部分借鑒了其中的內(nèi)容。

基礎(chǔ)概念

  • 字符
    字符是各種文字和符號(hào)的總稱,包括各國家文字、標(biāo)點(diǎn)符號(hào)、圖形符號(hào)、數(shù)字等,甚至還可以包括無法顯示的字符(比如ASCII標(biāo)準(zhǔn)定義了128個(gè)字符,其中33個(gè)字符無法顯示)。
  • 字符集
    為了使計(jì)算機(jī)能夠處理字符信息,首先要決定選取哪些字符。這樣就形成了一個(gè)集合,或者說一個(gè)表,稱為字符表(character repertoire)。當(dāng)然,也可以認(rèn)為這就是一個(gè)字符集(character set)。
    例如,將所有的英文字母放在一起可以組成一個(gè)字符集,將所有的漢字放在一起可以組成一個(gè)字符集,等等。
  • 編碼字符集
    對一個(gè)字符集中的所有字符進(jìn)行編號(hào),這種編號(hào)后的字符集叫做編碼字符集(這里的編碼僅僅指編號(hào),不同于下文中的編碼)。常見的編碼字符集有ASCII、Unicode、GBK等。

可以這樣來理解:字符串是由字符構(gòu)成,字符在計(jì)算機(jī)硬件中通過二進(jìn)制形式存儲(chǔ),這種二進(jìn)制形式就是編碼。如果直接使用 “字符串?字符?二進(jìn)制表示(編碼)” ,會(huì)增加不同類型編碼之間轉(zhuǎn)換的復(fù)雜性。所以引入了一個(gè)抽象層,“字符串?字符?與存儲(chǔ)無關(guān)的表示?二進(jìn)制表示(編碼)” ,這樣就可以用一種與存儲(chǔ)無關(guān)的形式表示字符,不同的編碼之間轉(zhuǎn)換時(shí)可以先轉(zhuǎn)換到這個(gè)抽象層,然后再轉(zhuǎn)換為其他編碼形式。
舉個(gè)例子:Unicode 就是 “與存儲(chǔ)無關(guān)的表示”,UTF-8 就是 “二進(jìn)制表示”。

ASCII字符集

通常說的ASCII字符集不包括擴(kuò)展集,只有128個(gè)字符,因此其編碼的存儲(chǔ)只需要使用7個(gè)bit,一個(gè)字節(jié)足夠了最高位永遠(yuǎn)都是0。比如字符'0',其代碼是十六進(jìn)制的0x30,二進(jìn)制表示為00110000。

這里說個(gè)和ASCII字符集有關(guān)的編碼方案,GSM標(biāo)準(zhǔn)協(xié)議規(guī)定單條短信最多存儲(chǔ)140個(gè)字節(jié)的內(nèi)容,如果短信內(nèi)容只包含ASCII字符,因?yàn)锳SCII字符的數(shù)據(jù)首位必定是0,所以GSM標(biāo)準(zhǔn)中規(guī)定了7bit編碼的短信格式,只用7個(gè)bit來連續(xù)存儲(chǔ)ASCII字符,這樣原本140個(gè)8bit的存儲(chǔ)空間,就可以存儲(chǔ)160個(gè)7bit的數(shù)據(jù)。如果誰現(xiàn)在手上還有NOKIA手機(jī),可以拿起來看看只包含ASCII字符的短信是不是可以輸入160個(gè)字符,如果短信中包含了非ASCII字符(比如中文),那么所有字符都會(huì)變成雙字節(jié)存儲(chǔ)的編碼(UCS-2編碼),一條短信的內(nèi)容就恢復(fù)成只能發(fā)送70個(gè)字符?,F(xiàn)如今的智能手機(jī)大多都支持短信拼接,并不是說單條短信的容量增加了,而是將你編寫的超過單條短信容量的短信分成多條發(fā)送,運(yùn)營商也是按多條短信計(jì)的。

GB系列字符集

中文環(huán)境下如果要正常顯示字符,僅依靠ASCII字符集是不行的,因此我們國家制訂了一系列的國標(biāo)(GB),其中就包括GB2312、GB13000、GBK、GB18030......,最新的標(biāo)準(zhǔn)是GB18030,包含70244個(gè)字符。

Unicode字符集

Unicode字符集由多語言軟件制造商組成的統(tǒng)一碼聯(lián)盟(Unicode Consortium)與國際標(biāo)準(zhǔn)化組織的ISO-10646工作組制訂,為各種語言中的每個(gè)字符指定統(tǒng)一且唯一的代碼點(diǎn),以滿足跨語言、跨平臺(tái)轉(zhuǎn)換和處理文本的要求。中、日、韓的三種文字占用了Unicode中0x3000到0x9FFF的部分 Unicode目前普遍采用的是UCS-2編碼,它用兩個(gè)字節(jié)來編碼一個(gè)字符, 比如漢字"一"的編碼是0x4E00。事實(shí)上Unicode對漢字支持不怎么好,這也是沒辦法的,簡體和繁體總共有六七萬個(gè)漢字,而UCS-2最多能表示65536個(gè),所以Unicode只能排除一些幾乎不用的漢字,好在GB2312字符集中常用的簡體漢字也不過6763個(gè),為了能表示所有漢字,Unicode也有UCS-4規(guī)范,就是用 4個(gè)字節(jié)來編碼字符。

Unicode代碼點(diǎn)范圍為0x00x10FFFF,共計(jì)1114112個(gè)代碼點(diǎn),劃分為編號(hào)016的17個(gè)字符平面,每個(gè)平面包含65536個(gè)代碼點(diǎn)。其中編號(hào)為0的平面最為常用,稱為基本多語種平面(Basic Multilingual Plane, BMP);其他平面則稱為輔助語言平面。

為了描述一個(gè)代碼點(diǎn),可以采用U加十六進(jìn)制整數(shù)的方法。比如,U+0041表示英文大寫字母A,U+4E00表示漢字”一”。

編碼

關(guān)于編碼方式,當(dāng)然可以采用類似ASCII字符集的編碼方式——代碼點(diǎn)等值轉(zhuǎn)換法(這是我自己起的名字)。既然Unicode代碼點(diǎn)的值的范圍是0~0x10FFF,那么可以用一個(gè)21bit的編碼單元來編碼,直接把代碼點(diǎn)等值轉(zhuǎn)換成21bit的二進(jìn)制序列。

但是這存在一個(gè)空間使用的問題,例如對于使用英語的人而言,ASCII基本可以滿足使用。如果使用ASCII碼,只需要1個(gè)字節(jié)來存儲(chǔ)字符,但是若使用剛才的思路,需要將近3個(gè)字節(jié)來存儲(chǔ),這顯然是浪費(fèi)空間的。

如果需要支持的字符集再少一些,僅支持編號(hào)0的平面,那至少也有65535個(gè)字符,需要16bit的空間(2字節(jié))來存儲(chǔ)一個(gè)字符,即UCS-2編碼,這種編碼用來存儲(chǔ)ASCII字符也是一種浪費(fèi)。

Unicode在很長一段時(shí)間內(nèi)無法推廣,直到互聯(lián)網(wǎng)的出現(xiàn),為解決Unicode如何在網(wǎng)絡(luò)上傳輸?shù)膯栴},于是面向傳輸?shù)谋姸?UTF(UCS Transfer Format)標(biāo)準(zhǔn)出現(xiàn)了,顧名思義,UTF-8就是每次8個(gè)位傳輸數(shù)據(jù),而UTF-16就是每次16個(gè)位。UTF-8就是在互聯(lián)網(wǎng)上使用最廣的一種Unicode的實(shí)現(xiàn)方式,這是為傳輸而設(shè)計(jì)的編碼,并使編碼無國界,這樣就可以顯示全世界上所有文化的字符了。

UTF-8

UTF-8最大的一個(gè)特點(diǎn),就是它是一種變長的編碼方式。它可以使用1~4個(gè)字節(jié)表示一個(gè)符號(hào),根據(jù)不同的符號(hào)而變化字節(jié)長度,當(dāng)字符在ASCII 碼的范圍時(shí),就用一個(gè)字節(jié)表示,保留了ASCII字符一個(gè)字節(jié)的編碼做為它的一部分,注意的是Unicode一個(gè)中文字符占2個(gè)字節(jié),而UTF-8一個(gè)中 文字符占3個(gè)字節(jié))。從Unicode到UTF-8并不是直接的對應(yīng),而是要過一些算法和規(guī)則來轉(zhuǎn)換。

Unicode符號(hào)范圍(十六進(jìn)制) UTF-8編碼方式(二進(jìn)制)
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

舉個(gè)例子:
還是用中文“一”,其Unicode值為0x4E00,落在0800-FFFF的范圍內(nèi),事實(shí)上中文基本上都在這個(gè)區(qū)域。0x4E00的二進(jìn)制表示為01001110 00000000,轉(zhuǎn)換成UTF-8就是11100100 10111000 10000000,對應(yīng)的十六進(jìn)制表示是0xE4 0xB8 0x80。

UTF-16

UTF-16的編碼單元是16bit,對于每個(gè)代碼點(diǎn),采用1個(gè)或者2個(gè)編碼單元來表示,因此這是一個(gè)變長表示。

UTF-32

UTF-32采用代碼點(diǎn)等值轉(zhuǎn)換法,將每個(gè)代碼點(diǎn)編碼為1個(gè)32bit的編碼單元(四字節(jié)),因此空間效率較低,不如其它Unicode編碼應(yīng)用廣泛。

工具推薦

這里有個(gè)網(wǎng)站提供的轉(zhuǎn)碼效果非常好,比起某些站長工具更標(biāo)準(zhǔn)。

Paste_Image.png

上圖是使用該網(wǎng)站查找中文字符“一”的結(jié)果,可以看到其Unicode值為U+4E00,UTF-8編碼為0xE4 0xB8 0x80,在URL中需要轉(zhuǎn)碼為%E4%B8%80,在js腳本中則是\u4e00......。

舉幾個(gè)栗子

有道翻譯

用有道翻譯的API來做演示,我們通過API獲取單詞"word"的中文翻譯。

GET http://fanyi.youdao.com/openapi.do?keyfrom=WoxLauncher&key=1247918016&type=data&doctype=json&version=1.1&q=word

返回的JSON如下所示:

{
  "translation": [
    "詞"
  ],
  "basic": {
    "us-phonetic": "w?d",
    "phonetic": "w??d",
    "uk-phonetic": "w??d",
    "explains": [
      "n. [語] 單詞;話語;消息;諾言;命令",
      "vt. 用言辭表達(dá)",
      "n. (Word)人名;(英)沃德"
    ]
  },
  "query": "word",
  "errorCode": 0,
  "web": [
    {
      "value": [
        "單詞",
        "字",
        "字 (計(jì)算機(jī))"
      ],
      "key": "word"
    },
    {
      "value": [
        "構(gòu)詞法",
        "造詞法",
        "詞性轉(zhuǎn)換"
      ],
      "key": "Word Formation"
    },
    {
      "value": [
        "關(guān)鍵字",
        "中心詞",
        "關(guān)鍵詞"
      ],
      "key": "key word"
    }
  ]
}

返回的內(nèi)容包含中文,從響應(yīng)頭我們可以看到返回的JSON使用了UTF-8編碼:

Paste_Image.png

用Wireshark抓包看看具體內(nèi)容:

Paste_Image.png

如上圖所示,translation這個(gè)key對應(yīng)的數(shù)組內(nèi)容應(yīng)該是"詞",所以我們看到高亮區(qū)域的內(nèi)容是22 E8 AF 8D 22,0x22對應(yīng)ASCII字符",0xE8 0xAF 0x8D正是中文的UTF-8編碼。

ONE·一個(gè)

這個(gè)API是通過抓包抓出來的,誰讓他們不走h(yuǎn)ttps呢?

GET http://v3.wufazhuce.com:8000/api/reading/index

返回?cái)?shù)據(jù)太多,僅截取一小段進(jìn)行分析。

{
  "res": 0,
  "data": {
    "essay": [
      {
        "content_id": "2176",
        "hp_title": "軟糖| “白日夢” _ 初夏的味道",
        "hp_makettime": "2017-04-03 06:00:00",
        "guide_word": "我們每周會(huì)選擇一個(gè)主題,由七個(gè)作者繪制不同風(fēng)格的短篇漫畫,每天一幅。",
        "start_video": "",
        "author": [
          {
            "user_id": "7742828",
            "user_name": "雙麒_宋 ",
            "desc": "因愛而畫,美好的作品產(chǎn)生于最壓抑的欲望。",
            "wb_name": "",
            "is_settled": "0",
            "settled_type": "0",
            "summary": "因愛而畫,美好的作品產(chǎn)生于最壓抑的欲望。",
            "fans_total": "574",
            "web_url": "http://image.wufazhuce.com/FoPpyeue8ajoRlZ4Fy39a56o4NO-"
          }
        ],
    ......
    }
......
}

返回的內(nèi)容包含中文,但從響應(yīng)頭我們看不到返回的JSON使用了什么編碼格式:

Paste_Image.png

用Wireshark抓包看看具體內(nèi)容:

Paste_Image.png

如上圖所示,這個(gè)API請求返回的JSON數(shù)據(jù)輸出的是中文的Unicode轉(zhuǎn)義字符,這其實(shí)也是JS對中文的標(biāo)準(zhǔn)處理方式,猜測后臺(tái)可能是NodeJS實(shí)現(xiàn)的。

我是咕咕雞,一個(gè)還在不停學(xué)習(xí)的全棧工程師。
熱愛生活,喜歡跑步,家庭是我不斷向前進(jìn)步的動(dòng)力。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容