區別:字符集 字符編碼 存儲編碼 ASCII Unicode UCS UTF ANSI UTF-8 UTF16 UTF32 GB2312 GBK GB18030 BIG-5

開發過程中,使用文本文檔、Word文檔過程中,經常會遇到亂碼,這些亂碼是怎麼產生的?

字符存儲都與哪些因素有關?

經常聽到的字符集、字符編碼是什麼?文件存儲編碼是什麼?

Windows記事本另存爲時可以選擇:ANSI、Unicode、Unicode Big Ending、UTF-8,分別表示什麼?如何轉換?Windows系統如何區分一個txt文檔是什麼編碼的?


基本概念

  • 字符(Character) 是各種文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。
  • 字符集(Character set) 是一個系統支持的所有抽象字符的集合。通常以二維表的形式存在,二維表的內容和大小是由使用者的語言而定。如ASCII,GBxxx,Unicode等。
  • 字符編碼(Character encoding) 是把字符集中的字符編碼爲特定的二進制數,以便在計算機中存儲。每個字符集中的字符都對應一個唯一的二進制編碼。

字符集和字符編碼一般都是成對出現的,如ASCII、IOS-8859-1、GB2312、GBK,都是即表示了字符集又表示了對應的字符編碼。Unicode比較特殊,有多種字符編碼(UTF-8,UTF-16等)

ASCII單字節-->ANSI多字節-->UNICODE寬字節

二進制

機器碼

字符集:一種或多種語言中字符的集合,每個字符有個字符編號,字符集:ASCII、Unicode、GB2312、GBK

字符編號:字符在字符集中的索引號,ANSI、Unicode(UCS-2、UCS-4)

字符編碼(即存儲編碼):在計算機內存中的編碼格式,由字符編號轉化而來,如UTF-8、UTF-16、UTF-32

Unicode:包含全球各種語言累計百萬字符的字符集,給全球字符設定規則並排了個序

UTF8:變長字符編碼,單字節表示ASCII碼,中文一般用3字節表示

GB2312:《信息交換用漢字編碼字符集》國標2312-1980,基本集共收入漢字6763個和非漢字圖形字符682個。整個字符集分成94個區,每區有94個位。每個區位上只有一個字符,因此可用所在的區和位來對漢字進行編碼,稱爲區位碼。把換算成十六進制的區位碼加上2020H,就得到國標碼。國標碼加上8080H,就得到常用的計算機機內碼。

GBK:全稱《漢字內碼擴展規範》(GBK即“國標”、“擴展”漢語拼音的第一個字母,GBK 向下與 GB 2312 編碼兼容,向上支持 ISO 10646.1國際標準

ASCII:美國製定的一套字符編碼,對英語字符與二進制位之間的關係,做了統一規定。ASCII 碼一共規定了128個字符的編碼

從ASCII、GB2312、GBK到GB18030,這些編碼方法是向下兼容的,即同一個字符在這些方案中總是有相同的編碼,後面的標準支持更多的字符。在這些編碼中,英文和中文可以統一地處理。區分中文編碼的方法是高字節的最高位不爲0。按照程序員的稱呼,GB2312、GBK到GB18030都屬於雙字節字符集 (DBCS)。

Unicode的學名是"Universal Multiple-Octet Coded Character Set",簡稱爲UCS。UCS可以看作是"Unicode Character Set"的縮寫。 編碼字符集的標準有兩個ANSI和Unicode,ANSI每個國家有自己的實現,其編碼與使用區域掛鉤。unicode爲表示現在已知的任何符號,但是爲了傳輸和存儲方便,產生了不同的實現方式utf-8,utf-16,utf-32。

Unicode 可以使用的編碼有三種:

UFT-8:一種變長的編碼方案,使用 1~6 個字節來存儲;
UFT-32:一種固定長度的編碼方案,不管字符編號大小,始終使用 4 個字節來存儲;
UTF-16:介於 UTF-8 和 UTF-32 之間,使用 2 個或者 4 個字節來存儲,長度既固定又可變。

UTF 是 Unicode Transformation Format 的縮寫,意思是“Unicode轉換格式”,後面的數字表明至少使用多少個比特位(Bit)來存儲字符。UCS規定了怎麼用多個字節表示各種文字。而由UTF(UCS Transformation Format)規範規定怎樣傳輸存儲這些編碼,是,常見的UTF規範包括UTF-8、UTF-7、UTF-16。

1) UTF-8
UTF-8 的編碼規則很簡單:如果只有一個字節,那麼最高的比特位爲 0;如果有多個字節,那麼第一個字節從最高位開始,連續有幾個比特位的值爲 1,就使用幾個字節編碼,剩下的字節均以 10 開頭。

具體的表現形式爲:

0xxxxxxx:單字節編碼形式,這和 ASCII 編碼完全一樣,因此 UTF-8 是兼容 ASCII 的;
110xxxxx 10xxxxxx:雙字節編碼形式;
1110xxxx 10xxxxxx 10xxxxxx:三字節編碼形式;
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字節編碼形式。

xxx 就用來存儲 Unicode 中的字符編號。字符編號與編碼之間就有了映射關係。

下面是一些字符的編碼實例(綠色部分表示本來的 Unicode 編號):

字符    N    æ    ⻬
Unicode 編號(二進制)    01001110    11100110    00101110 11101100
Unicode 編號(十六進制)    4E    E6    2E EC
UTF-8 編碼(二進制)    01001110    11000011 10100110    11100010 10111011 10101100
UTF-8 編碼(十六進制)    4E    C3 A6    E2 BB AC
對於常用的字符,它的 Unicode 編號範圍是 0 ~ FFFF,用 1~3 個字節足以存儲,只有及其罕見,或者只有少數地區使用的字符才需要 4~6個字節存儲。

2) UTF-32
UTF-32 是固定長度的編碼,始終佔用 4 個字節,足以容納所有的 Unicode 字符,所以直接存儲 Unicode 編號即可,不需要任何編碼轉換。浪費了空間,提高了效率。

3) UTF-16
UFT-16 比較奇葩,它使用 2 個或者 4 個字節來存儲。

對於 Unicode 編號範圍在 0 ~ FFFF 之間的字符,UTF-16 使用兩個字節存儲,並且直接存儲 Unicode 編號,不用進行編碼轉換,這跟 UTF-32 非常類似。

對於 Unicode 編號範圍在 10000~10FFFF 之間的字符,UTF-16 使用四個字節存儲,具體來說就是:將字符編號的所有比特位分成兩部分,較高的一些比特位用一個值介於 D800~DBFF 之間的雙字節存儲,較低的一些比特位(剩下的比特位)用一個值介於 DC00~DFFF 之間的雙字節存儲。

如果你不理解什麼意思,請看下面的表格:

Unicode 編號範圍
(十六進制)    具體的 Unicode 編號
(二進制)    UTF-16 編碼    編碼後的
字節數
0000 0000 ~ 0000 FFFF    xxxxxxxx xxxxxxxx    xxxxxxxx xxxxxxxx    2
0001 0000---0010 FFFF    yyyy yyyy yyxx xxxx xxxx    110110yy yyyyyyyy 110111xx xxxxxxxx    4

位於 D800~0xDFFF 之間的 Unicode 編碼是特別爲四字節的 UTF-16 編碼預留的,所以不應該在這個範圍內指定任何字符。如果你真的去查看 Unicode 字符集,會發現這個區間內確實沒有收錄任何字符。

UTF-16 要求在制定 Unicode 字符集時必須考慮到編碼問題,所以真正的 Unicode 字符集也不是隨意編排字符的。

總結
只有 UTF-8 兼容 ASCII,UTF-32 和 UTF-16 都不兼容 ASCII,因爲它們沒有單字節編碼。

GB2312、GBK、Shift-JIS 等特定國家的字符集都是在 ASCII 的基礎上發展起來的,它們都兼容 ASCII,所以只能採用變長的編碼方案:用一個字節存儲 ASCII 字符,用多個字節存儲本國字符。

以 GB2312 爲例,該字符集收錄的字符較少,所以使用 1~2 個字節編碼

對於 ASCII 字符,使用一個字節存儲,並且該字節的最高位是 0;
對於中國的字符,使用兩個字節存儲,並且規定每個字節的最高位都是 1。

由於單字節和雙字節的最高位不一樣,所以很容易區分一個字符到底用了幾個字節。
 

一般情況下用無BOM的形式吧,除非有問題的時候,再考慮換有BOM的。Windows系統保存的都是有BOM的,所以你可以看到,用記事本保存一個UTF-8的txt,其實是有BOM的,這一點需要注意。另外不同的文本編輯器對於有無BOM的稱呼也略有不同,比如EditPlus,有BOM的稱爲UTF-8+,無BOM的稱爲UTF-8,而在Notepad++中,有BOM的被稱爲標準UTF-8,而無BOM則被稱爲UTF-8無BOM。

UTF-8編碼的文本文檔,有的帶有BOM (Byte Order Mark, 字節序標誌),即0xEF, 0xBB, 0xBF,有的沒有。Windows下的txt文本編輯器在保存UTF-8格式的文本文檔時會自動添加BOM到文件頭。在判斷這類文檔時,可以根據文檔的前3個字節來進行判斷。然而BOM不是必需的,而且也不是推薦的。對不希望UTF-8文檔帶有BOM的程序會帶來兼容性問題,例如Java編譯器在編譯帶有BOM的UTF-8源文件時就會出錯。而且BOM去掉了UTF-8一個期望的特性,即是在文本全部是ASCII字符時UTF-8是和ASCII一致的,即UTF-8向下兼容ASCII。

在具體判斷時,如果文檔不帶有BOM,就無法根據BOM做出判斷,而且IsTextUnicode API也無法對UTF-8編碼的Unicode字符串做出判斷。那在編程判斷時就要根據UTF-8字符編碼的規律進行判斷了。

BOM,英文名是ByteOrderMark,用它來表示當前這個文本格式,其對應的關係如下(圖來自於百度百科)。

 

ANSI 的編碼格式爲什麼可以顯示中文?在簡體中文系統下,ANSI 編碼代表 GB2312 編碼,是計算機可以識別的編碼,適用於漢字處理、漢字通信等系統之間的信息交換。漢字的Ansi是這樣的,用兩個ANSI碼來表示一個漢字,這時第一個最高位爲標誌位,當爲1的時候就認爲當前ANSI碼和後邊緊接的一個ANSI碼兩個組合起來來表示一個漢字.而普通的Ansi,每個編碼高位都是0。所以,如果遇到1開頭的,軟件就知道,它與後面的Ansi組成了一個漢字

ANSI編碼
(本地化)

爲使計算機支持更多語言,通常使用 0x80~0xFF 範圍的 2 個字節來表示 1 個字符。比如:漢字 '中' 在中文操作系統中,使用 [0xD6,0xD0] 這兩個字節存儲。

在ASCII基礎上,不同的國家和地區制定了不同的標準,由此產生了 GB2312, BIG5, JIS 等各自的編碼標準。這些使用 2 個字節來代表一個字符的各種漢字延伸編碼方式,稱爲 ANSI 編碼在簡體中文系統下,ANSI 編碼代表 GB2312 編碼,在日文操作系統下,ANSI 編碼代表 JIS 編碼。不同 ANSI 編碼之間互不兼容,當信息在國際間交流時,無法將屬於兩種語言的文字,存儲在同一段 ANSI 編碼的文本中。

ANSI 確實是遺留編碼,在不同語言的系統中編碼不同,這一部分在微軟的術語中叫 code page。比如所謂 GBK 編碼,實際上更多地被叫做 CP936。

其實ANSI並不是某一種特定的字符編碼,而是在不同的系統中,ANSI表示不同的編碼。你的美國同事Bob的系統中ANSI編碼其實是ASCII編碼(ASCII編碼不能表示漢字,所以漢字爲亂碼),而你的系統中(“漢字”正常顯示)ANSI編碼其實是GBK編碼,而韓文系統中(“한국어”正常顯示)ANSI編碼其實是EUC-KR編碼。

那麼Windows系統是如何區分ANSI背後的真實編碼的呢?

微軟用一個叫“Windows code pages”(在命令行下執行chcp命令可以查看當前code page的值)的值來判斷系統默認編碼,比如:簡體中文的code page值爲936(它表示GBK編碼,win95之前表示GB2312,詳見:Microsoft Windows' Code Page 936),繁體中文的code page值爲950(表示Big-5編碼)。

我們能否通過修改Windows code pages的值來改變“ANSI編碼”呢?命令提示符下,我們可以通過chcp命令來修改當前終端的active code page,例如:
(1) 執行:chcp 437,code page改爲437,當前終端的默認編碼就爲ASCII編碼了(漢字就成亂碼了);
(2) 執行:chcp 936,code page改爲936,當前終端的默認編碼就爲GBK編碼了(漢字又能正常顯示了)。
上面的操作只在當前終端起作用,並不會影響系統默認的“ANSI編碼”。(更改命令行默認codepage參看:設置cmd的codepage的方法)。

Windows下code page是根據當前系統區域(locale)來設置的,要想修改系統默認的“ANSI編碼”,我們可以通過修改系統區域來實現(“控制面板” =>“時鐘、語言和區域”=>“區域和語言”=>“管理”=>“更改系統區域設置...”)

如何判斷一個文本文件是無BOM 的UTF8 編碼 還是 ANSI編碼?

也就是如何區分寬字節字符串與多字節字符串?

關於GBK編碼的BUG

很多細心的人會發現,新建一個空的文本文件,用記事本打開(必須是Windows自帶的記事本),只輸入“聯通”二字保存關閉(輸入“1聯通”也是聯通顯示的也是亂碼),再重新打開時將是亂碼。當txt文檔中一切字符都在 C0≤AA(第一個字節)≤DF 80≤BB(第二個字節)≤BF 這個範圍時,notepad都無法確認文檔的格式,自動依照GB-2312來解碼。 而"聯通"就是C1 AA CD A8,剛好在上面的範圍內,所以不能正常顯現。記事本默認是以ANSI編碼保存文本文檔的,而正是這種編碼存在的bug招致了上述怪現象。假如保存時選擇Unicode、Unicode (Big Endian)、UTF-8編碼,就正常了。

Windows記事本要支持Unicode,但是有一個問題,一段二進制編碼,如何確定它是GBK還是BIG5還是UTF-16/UTF-8?

記事本的做法是在TXT文件的最前面保存一個標籤,如果記事本打開一個TXT,發現這個標籤,就說明是unicode。標籤叫BOM,如果是0xFF 0xFE,是UTF16LE,如果是0xFE 0xFF則UTF16BE,如果是0xEF 0xBB 0xBF,則是UTF-8。如果沒有這三個東西,那麼就是ANSI,使用操作系統的默認語言編碼來解釋。Unicode的好處就是,不論你的TXT放到什麼語言版本的Windows上,都能正常顯示。而ANSI編碼則不能。

參考資料:

http://www.regexlab.com/zh/encoding.htm

https://pcedu.pconline.com.cn/empolder/gj/other/0505/616631.html

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

https://blog.csdn.net/xjz729827161/article/details/53064653

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章