前幾天有個同學問我:“老師,為什麼我按著別的文檔寫的一模一樣的,但我卻是亂碼呢?”
其實很簡單,因為你的編碼跟和人家不一樣!
我們電腦中編碼一般分為五種:
1.ANSI
2.UTF-16LE
3.UTF-16BE
4.UTF-8
5.帶有BOM的UTF-8
UTF-16BE、UTF-16LE、UTF-16 三者之間的區別
UTF-16BE,其後綴是 BE 即 big-endian,大端的意思。大端就是將高位的字節放在低地址表示。
UTF-16LE,其後綴是 LE 即 little-endian,小端的意思。小端就是將高位的字節放在高地址表示。
UTF-16,沒有指定後綴,即不知道其是大小端,所以其開始的兩個字節表示該字節數組是大端還是小端。即FE FF表示大端,FF FE表示小端。
一、ANSI
ANSI(American National Standards Institute),中文:美國國家標準學會。
為使計算機支持更多語言,通常使用 0x80~0xFF 範圍的多個字節來表示 1 個字符。比如:漢字 '中' 在簡體中文Windows操作系統中,使用 [0xD6,0xD0] 這兩個字節存儲。對於ANSI編碼而言,0x00~0x7F之間的字符,依舊是1個字節代表1個字符。這一點是ANSI編碼與UTF-16編碼之間最大也最明顯的區別。比如"A君是第131號",在ANSI編碼中,佔用12個字節,而在UTF-16編碼中,佔用16個字節。因為A和1、3、1這4個字符,在ANSI編碼中只各佔1個字節,而在UTF-16編碼中,是需要各佔2個字節的。
記事本默認是以ANSI編碼保存文本文檔的,而正是這種編碼存在的bug招致了上述怪現象。假如保存時選擇Unicode、Unicode (Big Endian)、UTF-8編碼,就正常了。此外,假如以ANSI編碼保存含有某些特別符號的文本文檔,再次打開後符號也會變成英文問號。
二、UTF-16LE,UTF-16BE
Unicode 是 unicode.org 制定的編碼標準,目前得到了絕大部分操作系統和編程語言的支持。unicode.org 官方對 Unicode 的定義是:Unicode provides a unique number for every character, no matter what the platform, no matter what the program, no matter what the language。可見,Unicode 所做的是為每個字符定義了一個相應的數字表示。比如,“a”的 Unicode 值是 0x0061,“一”的 Unicode 值是 0x4E00,這是最簡單的情況,每個字符用2個字節表示。
unicode.org 定義了百萬個以上的字符,如果將所有的字符用統一的格式表示,需要的是 4 個字節。“a”的 Unicode 表示就會變成 0x00000061,而“一“的 Unicode 值是 0x00004E00。實際上,這就是 UTF32,Linux 操作系統上所使用的 Unicode 方案。
但是,仔細分析可以發現,其實絕大部分字符只使用 2 個字節就可以表示了。英文的 Unicode 範圍是 0x0000-0x007F,中文的 Unicode 範圍是 0x4E00-0x9F**,真正需要擴展到 4 個字節來表示的字符少之又少,所以有些系統直接使用 2 個字節來表示 Unicode。比如 Windows 系統上,Unicode 就是兩個字節的。對於那些需要 4 個字節才能表示的字符,使用一種代理的手法來擴展(其實就是在低兩個字節上做一個標記,表示這是一個代理,需要連接上隨後的兩個字節,才能組成一個字符)。這樣的好處是大量的節約了存取空間,也提高了處理的速度。這種 Unicode 表示方法就是 UTF16。一般在 Windows 平臺上,提到 Unicode,那就是指 UTF16 了。
至於 UTF16-LE 和 UTF16-BE,則與計算機的 CPU 構架有關。LE 指 Little Endian,而 BE 指 Big Endian。由於 UTF16 是雙字節編碼,所以兩個字節保存時哪個在前,哪個在後關係到解析出字符的結果。至於為什麼會出現 BE 和 LE 的編碼,則是由於歷史原因造成的:在 Mac 和 PC 機上,對字節順序的理解是不一致的。如果一個文件不明確說明 UTF16 使用的是 BE 還是 LE,那麼就需要通過 BOM 來指明瞭。我們一般的 X86 系統都是 Little Endian 的,可以認為 UTF16=UTF16-LE。
四、UTF-8
由於對於歐洲和北美,實際上使用的編碼範圍在 0x0000-0x00FF 之間,只需要一個字符就可以表示所有的字符。即使是使用 UTF16 來作為內存的存取方式,還是會帶來巨大的空間浪費,因此就有了 UTF8 的編碼方式。UTF8 是一個可變長度字符編碼,它同時是一個前綴碼,前綴碼的特徵是,編碼系統中的任意一個合法的碼不會是另外一個碼的前綴,所以 UTF8 不需要指定字節序。一個 UTF8 編碼可以用 1~6 個字節來表示,將第一個字節的前幾個比特設置為 1 來指定這個字符佔用幾個比特,比如一個兩字節的字符的編碼,第一位是 110xxxxx,第二位是 10xxxxxx,而一個六字節字符的編碼是這樣的:1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx,所以 UTF-8 最多能編碼 231 個字符。所以對於只需要1個字節的字符,就使用一個字節。對於中日韓等原本需要兩個字節才能表示的字符,則通過一個UTF16-UTF8 的算法實現相互之間的轉換,一般需要 3 個字節才能表示。UTF8 使用的算法很有意思,大致映射關係如下:
Unicode編碼UTF-8編碼(二進制)U+0000 – U+007F0xxxxxxxU+0080 – U+07FF110xxxxx 10xxxxxxU+0800 – U+FFFF1110xxxx 10xxxxxx 10xxxxxxU+10000 – U+10FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
由於 UTF8 可以方便的轉換為 UTF16 和 UTF32,而且 UTF8 在每個操作系統平臺上的實現都是一樣的,也不存在跨平臺的問題,所以 UTF8 成為跨平臺的 Unicode 很好的解決方案。當然,對於中文來說,由於每個字符需要 3 個字節才能表示,還是有點浪費的。
5.帶有BOM的UTF-8
UTF-8 不需要 BOM,儘管 Unicode 標準允許在 UTF-8 中使用 BOM。
所以不含 BOM 的 UTF-8 才是標準形式
你下載的模板編碼很多都是帶BOM的UTM-8,只需要改下你的編碼就可以了。
最後,小編想說:我是一名python開發工程師,
整理了一套最新的python系統學習教程,
想要這些資料的可以關注私信小編“01”即可(免費分享哦)希望能對你有所幫助
閱讀更多 Python程序員不動否 的文章