字元位元組和編碼

  • 2020 年 11 月 10 日
  • 筆記

引言

前提概述

  • 「字元與編碼」是一個被經常討論的話題。即使這樣,時常出現的亂碼仍然困擾著大家。雖然我們有很多的辦法可以用來消除亂碼,但我們並不一定理解這些辦法的內在原理。而有的亂碼產生的原因,實際上由於底層程式碼本身有問題所導致的。因此,不僅是初學者會對字元編碼感到模糊,有的底層開發人員同樣對字元編碼缺乏準確的理解。

字元與編碼

發展概覽

系統內碼 說明 系統
階段一 ASCII 電腦剛開始只支援英語,其它語言不能夠在電腦上存儲和顯示。 英文 DOS
階段二 ANSI編碼 (本地化) 為使電腦支援更多語言,通常使用 0x80~0xFF 範圍的 2 個位元組來表示 1 個字元。比如:漢字 ‘中’ 在中文作業系統中,使用 [0xD6,0xD0] 這兩個位元組存儲。 不同的國家和地區制定了不同的標準,由此產生了 GB2312, BIG5, JIS 等各自的編碼標準。這些使用 2 個位元組來代表一個字元的各種漢字延伸編碼方式,稱為 ANSI 編碼。在簡體中文系統下,ANSI 編碼代表 GB2312 編碼,在日文作業系統下,ANSI 編碼代表 JIS 編碼。 不同 ANSI 編碼之間互不兼容,當資訊在國際間交流時,無法將屬於兩種語言的文字,存儲在同一段 ANSI 編碼的文本中。 中文 DOS,中文 Windows 95/98,日文 Windows 95/98
階段三 UNICODE (國際化) 為了使國際間資訊交流更加方便,國際組織制定了 UNICODE 字符集,為各種語言中的每一個字元設定了統一併且唯一的數字編號,以滿足跨語言、跨平台進行文本轉換、處理的要求。 Windows NT/2000/XP,Linux,Java

字元串在記憶體中的存放方式

  • 在 ASCII 階段,單位元組字元串使用一個位元組存放一個字元(SBCS)。比如,”Bob123″ 在記憶體中為:

    B(42)	o(6f)	b(62)	1(31)	2(32)	3(33)	\0(00)
    
  • 在使用 ANSI 編碼支援多種語言階段,每個字元使用一個位元組或多個位元組來表示(MBCS),因此,這種方式存放的字元也被稱作多位元組字元。比如,”中文123″ 在中文 Windows 95 記憶體中為7個位元組,每個漢字佔2個位元組,每個英文和數字字元佔1個位元組:

    中(D6 D0)    文(CE C4)    1(31)	  2(32)    3(33)    \0(00)
    
  • 在 UNICODE 被採用之後,電腦存放字元串時,改為存放每個字元在 UNICODE 字符集中的序號。目前電腦一般使用 2 個位元組(16 位)來存放一個序號(DBCS),因此,這種方式存放的字元也被稱作寬位元組字元。比如,字元串 “中文123” 在 Windows 2000 下,記憶體中實際存放的是 5 個序號:

     中(2D 4E)    文(87 65)	  1(31 00)    2(32 00)     3(33 00)	   \0(00 00) 
     
    在 x86 CPU 中,低位元組在前
    一共占 10 個位元組。
    

字元,位元組,字元串

概念描述 舉例
字元 人們使用的記號,抽象意義上的一個符號。 ‘1’, ‘中’, ‘a’, ‘$’, ‘¥’, ……
位元組 電腦中存儲數據的單元,一個8位的二進位數,是一個很具體的存儲空間。 0x01, 0x45, 0xFA, ……
ANSI 字元串 在記憶體中,如果「字元」是以 ANSI 編碼形式存在的,一個字元可能使用一個位元組或多個位元組來表示,那麼我們稱這種字元串為 ANSI 字元串或者多位元組字元串 “中文123” (佔7位元組)
UNICODE 字元串 在記憶體中,如果「字元」是以在 UNICODE 中的序號存在的,那麼我們稱這種字元串為 UNICODE 字元串或者寬位元組字元串 L”中文123″ (佔10位元組)
  • 注意!由於不同 ANSI 編碼所規定的標準是不相同的,因此,對於一個給定的多位元組字元串,我們必須知道它採用的是哪一種編碼規則,才能夠知道它包含了哪些「字元」。而對於 UNICODE 字元串來說,不管在什麼環境下,它所代表的「字元」內容總是不變的。

字符集與編碼

  • 各個國家和地區所制定的不同 ANSI 編碼標準中,都只規定了各自語言所需的「字元」。比如:漢字標準(GB2312)中沒有規定韓國語字元怎樣存儲。這些 ANSI 編碼標準所規定的內容包含兩層含義:
    1. 使用哪些字元。也就是說哪些漢字,字母和符號會被收入標準中。所包含「字元」的集合就叫做「字符集」。
    2. 規定每個「字元」分別用一個位元組還是多個位元組存儲,用哪些位元組來存儲,這個規定就叫做「編碼」。
  • 各個國家和地區在制定編碼標準的時候,「字元的集合」和「編碼」一般都是同時制定的。因此,平常我們所說的「字符集」,比如:GB2312, GBK, JIS 等,除了有「字元的集合」這層含義外,同時也包含了「編碼」的含義。
  • UNICODE 字符集」包含了各種語言中使用到的所有「字元」。用來給 UNICODE 字符集編碼的標準有很多種,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。

常見編碼

分類 編碼標準 說明
單位元組字元編碼 ISO-8859-1 最簡單的編碼規則,每一個位元組直接作為一個 UNICODE 字元。比如,[0xD6, 0xD0] 這兩個位元組,通過 iso-8859-1 轉化為字元串時,將直接得到 [0x00D6, 0x00D0] 兩個 UNICODE 字元,即 “ÖД。 反之,將 UNICODE 字元串通過 iso-8859-1 轉化為位元組串時,只能正常轉化 0~255 範圍的字元。
ANSI 編碼 GB2312, BIG5, Shift_JIS, ISO-8859-2 …… 把 UNICODE 字元串通過 ANSI 編碼轉化為「位元組串」時,根據各自編碼的規定,一個 UNICODE 字元可能轉化成一個位元組或多個位元組。 反之,將位元組串轉化成字元串時,也可能多個位元組轉化成一個字元。比如,[0xD6, 0xD0] 這兩個位元組,通過 GB2312 轉化為字元串時,將得到 [0x4E2D] 一個字元,即 ‘中’ 字。 「ANSI 編碼」的特點: 1. 這些「ANSI 編碼標準」都只能處理各自語言範圍之內的 UNICODE 字元。 2. 「UNICODE 字元」與「轉換出來的位元組」之間的關係是人為規定的。
UNICODE 編碼 UTF-8, UTF-16, UnicodeBig …… 與「ANSI 編碼」類似的,把字元串通過 UNICODE 編碼轉化成「位元組串」時,一個 UNICODE 字元可能轉化成一個位元組或多個位元組。 與「ANSI 編碼」不同的是: 1. 這些「UNICODE 編碼」能夠處理所有的 UNICODE 字元。 2. 「UNICODE 字元」與「轉換出來的位元組」之間是可以通過計算得到的。
  • 我們實際上沒有必要去深究每一種編碼具體把某一個字元編碼成了哪幾個位元組,我們只需要知道「編碼」的概念就是把「字元」轉化成「位元組」就可以了。對於「UNICODE 編碼」,由於它們是可以通過計算得到的,因此,在特殊的場合,我們可以去了解某一種「UNICODE 編碼」是怎樣的規則。