標籤:

能否詳細介紹一下字元編碼的發展歷史?

經常接觸到ansi、unicode和utf-8,想弄通透點。

可能搜索姿勢不對,越搜越亂,utf-7/utf-8/utf-16,有bom頭無bom頭,little endian/big endian,UCS-2和unicode和utf-16,UCS-4和utf-32…

希望可以結合發展歷史來詳細介紹一下字元編碼,十分感謝。


歷史不知道,你說的這些名詞倒是可以解釋一下。

經常接觸到ansi、unicode和utf-8

把這三個放在一起的,估計是Windows的記事本吧:

這是一個非常不直觀且非常非常不嚴謹的分類。

ANSI 是美國國家標準協會(American National Standards Institute)的縮寫,Windows當時使用這個詞大概是為了區別Unicode/UTF。就像 @劉松泉 說的,ANSI 不是一種具體的編碼,而是對一些遺留編碼的統稱。

在Windows記事本中,ANSI指的是當前的「本地字符集/編碼」,即你Windows的「本地語言」。在控制面板的「地區/語言」設置中,有一個稱為「非Unicode語言」的選項,該處設置的語言就是我所說的「本地語言」:

當「本地語言」設置成日語時,記事本中的ANSI指的是Shift_JIS;

當「本地語言」設置成簡體中文時,記事本中的ANSI指的是GBK;

當「本地語言」設置成繁體中文時,記事本中的ANSI指的是BIG5等等……

這些「本地語言」基本上都是由ASCII擴充的,在ASCII範圍內它們是一致的,在ASCII字元之後放入自己語言的字元擴充為當地的字符集(即Shift_JIS、GB2312/GBK等),用以顯示當地語言。在安放各語言字元時,各地按照自己的標準。所以當以A種字符集存儲了一些字元,卻以B種字符集顯示時,ASCII字元之後的部份會亂碼。

比如我以日本的Shift_JIS存儲了:

"Zhihu" 知乎

然後以簡體中文的GBK顯示,會變成:

"Zhihu" 抦屓

ASCII範圍內的英數和半形符號因為大家都一致,所以不存在問題。

但到了擴充的區域,大家排序和使用範圍不一樣了。日本的Shift_JIS將「乎」字放在0x8CC1上,以Shift_JIS存儲「乎」字時,存儲的是「本地編碼的0x8CC1」這個位置;若以GBK顯示的話,這個「本地編碼」變成了GBK,因為「GBK的0x8CC1」放的是「屓」字,所以顯示成了「屓」。

這是比較簡單,即都使用2位元組表示本地字元的情況。還有因為每個字元使用的位元組數不同導致更加混亂的情況,在此不再展開。總而言之,就是各地為了顯示自己語言的字元,擴充了僅有128個字元的ASCII,再後面以自己的規格加加加,造出了世界上那麼多的「本地字符集」以及相應的「本地編碼」。

當然本地字符集只優先考慮本地語言,比如中國的GBK沒有韓文,早期使用GBK的QQ,就無法發送韓文字元。因為你要發送的韓文字元無法映射到GBK字符集中,自然也不可能以GBK方式描述並傳輸它。

Unicode從字面上看是「單一編碼」的意思,即解決國際上不同地區各自有著不同編碼造成的混亂情況。Unicode是獨立於那些「基於ASCII擴充而成的各種本地字符集」的字符集的,已經包含世界上絕大多數語言,且一直在擴充。

Unicode跟各種「本地字符集」是並列的,但Unicode跟UTF不是並列的。在那些「本地字符集」中,編碼跟字符集混著說大概不要緊,因為他們大多是1對1的;但在Unicode中不行,Unicode有很多種編碼方式。

Unicode是一個字符集,UTF是對Unicode字符集的編碼。UTF全稱是Unicode Transformation Format,直譯就是「Unicode轉換格式」。Unicode負責給字元設置位置,比如「乎」字在0x4E4E,而UTF是把這個0x4E4E以某種方式傳輸。

Unicode包含的字元已經超過2個位元組可以表示的範圍了,超過的怎麼表示我不知道,只說在2個位元組可以表示的範圍內的情況。在這個範圍內,UTF-16是無論什麼字元都用2個位元組表示(即16bit)。但因為使用兩個位元組,存在高位低位順序的問題:

比如「知」字,Unicode是0x77E5。

UTF-16 Big Endian 存儲為 0x77 0xE5;

UTF-16 Little Endian 存儲為 0xE5 0x77;

這兩種存儲方式都存在,所以需要在起始處設置一個標記,來標記接下來它是「從高到低」的 Big Endian 還是「從低到高」的 Little Endian,這個東西就叫做BOM,全稱Byte Order Mark,即位元組順序標記。在UTF-16中它是必須的,不然不知道是以什麼順序去讀取。

UTF-16 Big Endian 的BOM是 0xFE 0xFF;

UTF-16 Little Endian 的BOM是 0xFF 0xFE;

於是,一個存了一個「知」字的 UTF-16 Big Endian 文本文件,用16進位編輯器打開是這樣的:

一個存了一個「知」字的 UTF-16 Little Endian 文本文件,用16進位編輯器打開是這樣的:

UTF-8是可變長度的,與ASCII相同的部份使用1位元組存儲,所以可以兼容ASCII,且因為ASCII字元佔用空間少,所以現在網頁很愛用。超過ASCII範圍的字元依次使用2位元組、3位元組等,然後以一些標記註明該字元使用多少位元組(具體怎麼算的我不懂),它沒有高低順序的區別,所以不需要BOM。但是還是可以有BOM(BOM寫成什麼我也不知道),所以UTF-8存在有無BOM的兩種。Windows記事本存儲為UTF-8時是有BOM的。

回到這張圖,Windows記事本存儲時的4個選項實際上分別代表:

ANSI → 當前的本地編碼

Unicode → UTF-16 Little Endian

Unicode big endian → UTF-16 Big Endian

UTF-8 → UTF-8 帶BOM

若需要以UTF-8無BOM等其他編碼方式存儲,需要使用其他文本編輯器,如notepad++等。

唔,先到這裡吧。


ANSI 不是一種具體的編碼,而是對一些遺留編碼的統稱。

這個就要從 ASCII 開始講起了……

各種遺留編碼,基本上都是在 ASCII 的基礎上擴充。

Unicode 並不是編碼,而是字符集。各 UTF 則是表示 Unicode 的編碼方法……

有一個特殊的 UTF,是 GB 18030,這是在遺留編碼上的擴充。


推薦閱讀:

」手機複製這幾個字看你多久能刪完」這句話有什麼秘密能讓人刪不完?
Unicode與bytes有何區別?
程序如何區分該顯示中文字元或英語字元?
字元在內存中最終的表示形式是什麼?是某種字元編碼還是碼位(Code Point)?
要往 Unicode 里添加符號需要經過什麼樣的流程?

TAG:字元編碼 |