關於文件編碼格式的一點探討
以前碰到過文件編碼問題,但都沒太在意。最近在win7下寫一個C++程序時,轉移到linux下表現怪異,調試個半天發現竟然是文件編碼問題!於是想花點時間好好總結一下關於文件編碼格式的基本概念。這東西長時間沒搞就容易忘,這也方便以後再來查詢。
首先,關於字符編碼的基本概念可以參考百度百科:https://baike.baidu.com/view/1204863.htm?fr=aladdin,當然我不清楚有多少正確性,不過我還是稍微總結了一些,最後再添加一個我自己的示例,若有誤請見諒!
ASCII
ASCII碼於1961年提出,用於在不同計算機硬件和軟件係統中實現數據傳輸標準化,在大多數的小型機和全部的個人計算機都使用此碼。ASCII碼劃分為兩個集合:128個字符的標準ASCII碼和附加的128個字符的擴展ASCII碼。標準ASCII碼為7位,擴展為8位。擴展的8位碼官方標準叫:ISO-8859-1,也叫Latin-1編碼
ANSI(MBCS)
為了擴充ASCII編碼,以用於顯示本國的語言,不同的國家和地區製定了不同的標準,由此產生了 GB2312, BIG5, JIS 等各自的編碼標準。這些使用2個字節來代表一個字符的各種漢字延伸編碼方式,稱為ANSI編碼,又稱為"MBCS(Multi-Bytes Character Set,多字節字符集)"。在簡體中文係統下,ANSI編碼代表GB2312編碼,在日文操作係統下,ANSI 編碼代表 JIS 編碼,所以在中文 windows下要轉碼成gb2312,gbk隻需要把文本保存為ANSI編碼即可。不同ANSI編碼之間互不兼容,導致了unicode碼的誕生。
我國的編碼
GB2312,1980年頒布,有區碼、位碼、存儲碼之分
GBK,兼容GB2312,添加了繁體字
GB18030,增加了更多字符
BIG5,台灣、香港地區使用,也叫大五碼
UNICODE
統一了全世界的所有字符,可分為UCS-2(對應UTF-16)和UCS-4(對應UTF-32),有大小端之分,如"A"的Unicode編碼為6500,而BigEndian Unicode編碼為0065,編碼效率不高
UTF
為了提高UNICODE編碼效率,就出現了UTF碼,變長字節。UTF-8兼容ASCII,UTF-16不兼容ASCII。
UCS-2與UTF-8字節流之間的對應
UCS-2編碼(16進製) UTF-8 字節流(二進製)
0000 - 007F 0xxxxxxx
0080 - 07FF 110xxxxx10xxxxxx
0800 - FFFF 1110xxxx10xxxxxx 10xxxxxx
例如“漢”字的Unicode編碼是6C49。6C49在0800-FFFF之間,所以肯定要用3字節模板了:1110xxxx10xxxxxx 10xxxxxx。將6C49寫成二進製是:0110 110001 001001,用這個比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。可見UTF-8是變長的,將Unicode編碼為0000-0007F的字符,用單個字節來表示; 0080-007FF的字符用兩個字節表示;0800-FFFF的字符用3字節表示。因為目前為止Unicode-16規範沒有指定FFFF以上的字符,所以UTF-8最多是使用3個字節來表示一個字符。
字節序
UTF-8以字節為編碼單元,沒有字節序的問題。UTF-16以兩個字節為編碼單元,在解釋一個UTF-16文本前,首先要弄清楚每個編碼單元的字節序。例如收到一個“奎”的Unicode編碼是594E,“乙”的Unicode編碼是4E59。如果我們收到UTF-16字節流“594E”,那麼這是“奎”還是“乙”?
Unicode規範中推薦的標記字節順序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一個有點小聰明的想法:
在UCS編碼中有一個叫做"ZEROWIDTH NO-BREAK SPACE"的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符,所以不應該出現在實際傳輸中。UCS規範建議我們在傳輸字節流前,先傳輸字符"ZERO WIDTH NO-BREAK SPACE"。
這樣如果接收者收到FEFF,就表明這個字節流是Big-Endian的;如果收到FFFE,就表明這個字節流是Little-Endian的。因此字符"ZEROWIDTH NO-BREAK SPACE"又被稱作BOM。
UTF-8不需要BOM來表明字節順序,但可以用BOM來表明編碼方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8編碼是EF BB BF。所以如果接收者收到以EF BB BF開頭的字節流,就知道這是UTF-8編碼了
不同的係統對BOM的支持
因為一些係統或程序不支持BOM,因此帶有BOM的Unicode文件有時會帶來一些問題。
1、JDK1.5以及之前的Reader都不能處理帶有BOM的UTF-8編碼的文件,解析這種格式的xml文件時,會拋出異常:Content is not allowed in prolog。
2、Linux/UNIX 並沒有使用 BOM,因為它會破壞現有的 ASCII 文件的語法約定。
3、不同的編輯工具對BOM的處理也各不相同。使用Windows自帶的記事本將文件保存為UTF-8編碼的時候,記事本會自動在文件開頭插入BOM(雖然BOM對UTF-8來說並不是必須的)。而其它很多編輯器用不用BOM是可以選擇的。UTF-8、UTF-16都是如此。
軟件如何決定文本的字符集與編碼
(1)對於Unicode文本最標準的途徑是檢測文本最開頭的幾個字節。如:
開頭字節 Charset/encoding
EF BB BF UTF-8
FE FF UTF-16/UCS-2,big endian(UTF-16BE)
FF FE UTF-16/UCS-2,little endian(UTF-16LE)
FF FE 00 00 UTF-32/UCS-4,little endian.
00 00 FE FF UTF-32/UCS-4,big-endian
(2)彈出一個對話框來請示用戶。
然而MBCS文本(ANSI)沒有這些位於開頭的字符集標記,現在很多軟件保存文本為Unicode時,可以選擇是否保存這些位於開頭的字符集標記。因此,軟件不應該依賴於這種途徑。這時,軟件可以采取一種比較安全的方式來決定字符集及其編碼,那就是彈出一個對話框來請示用戶。
(3)采取自己“猜”的方法。
如果軟件不想麻煩用戶,或者它不方便向用戶請示,那它隻能采取自己“猜”的方法,軟件可以根據整個文本的特征來猜測它可能屬於哪個charset,這就很可能不準了。使用記事本打開那個“聯通”文件就屬於這種情況。(把原本屬於ANSI編碼的文件當成UTF-8處理,詳細說明見:https://blog.csdn.net/omohe/archive/2007/05/29/1630186.aspx)
win7平台測試示例
win7下創建一個文件tmp.txt,內容為:
a<CR>
漢
即一個ascii字符,一換行,再加一中文字符。
我們知道在中文windows係統中,默認ANSI即為GB係列的字符集,不妨將tmp.txt更名為tmp-ansi.txt。那怎麼查呢?可用Emeditor打開它,在窗口右下角即可見所屬字符集,如下圖:
很明顯是GB2312字符集。那怎樣看具體的編碼呢?有幾種方式:
1、進入網站https://bianma.supfree.net/,輸入漢字即可得,如下圖:
2、單擊Emeditor菜單View→Character Code Value,即可顯示,同時還顯示了unicode編碼,如下圖:
3、用ultraedit打開,快捷鍵ctrl+h,即可顯示文件內容編碼為:61 0D 0A BA BA,如下圖:
同理,當我們將tmp-ansi.txt用記事本另存為其它格式時(當然用Emeditor另存為的可選格式更多),也可用上述方式得到驗證其unicode編碼為:6C49,UTF-8編碼為:E6 B1 89,如下圖驗證tmp-utf-8.txt(由記事本生成,默認帶有BOM):
即帶有簽名的BOM:EF BB BF; 'a':61; CRLF:0D 0A; '漢':E6 B1 89
再來看另存為unicode big endian的情況:
果然字節數多了!其BOM:FE FF; 接下來的每個字符為2個字節。當然若是小端的話,其BOM就是FF FE了
Linux平台測試示例
同理在Linux測試也差不多,隻不過軟件沒了,用命令可看。創建文件tmp-utf-8,同樣輸入:a+換行+漢,執行命令:
od -x tmp-utf-8



最後更新:2017-04-03 05:39:58