程序在處理文本的時候如何區分 [ANSI] 和 [無BOM的UTF8] 這兩種編碼?
UCS-4和UTF-8的對應關係:
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
UTF-8的二進位表達是非常有規律的,一般來講,如果你發現一個流可以被解釋成UTF-8,那麼就當他是UTF-8好了。當然了,你也可以同時獲取本地編碼,然後看看這個流是否也可以被解釋成本地編碼,因為並不是什麼字元都是合法的。但是後者做起來比較苦逼,你可以強行使用 MultiByteToWideChar function 函數測試一下,加上MB_ERR_INVALID_CHARS標記。
因此這就會有若干結果:- UTF-8合法本地編碼不合法,使用UTF-8
- UTF-8不合法本地編碼合法,使用本地編碼
- UTF-8合法本地編碼也合法,你把傳出來的UTF-16再轉UTF-8看看是不是一樣的:
- 一樣,那怎樣都可以
- 不一樣,彈框讓用戶選
我認為可能下面這段文字會對你有所啟發。轉載自字元編解碼的故事(ASCII,ANSI,Unicode,Utf-8區別)
講到這裡,我們再順便說說一個很著名的奇怪現象:當你在 windows 的記事本里新建一個文件,輸入"聯通"兩個字之後,保存,關閉,然後再次打開,你會發現這兩個字已經消失了,代之的是幾個亂碼!呵呵,有人說這就是聯通之所以拼不過移動的原因。
其實這是因為GB2312編碼與UTF8編碼產生了編碼衝撞的原因。
當一個軟體打開一個文本時,它要做的第一件事是決定這個文本究竟是使用哪種字符集的哪種編碼保存的。軟體一般採用三種方式來決定文本的字符集和編碼:
檢測文件頭標識,提示用戶選擇,根據一定的規則猜測
最標準的途徑是檢測文本最開頭的幾個位元組,開頭位元組 Charset/encoding,如下表:
EF BB BF UTF-8
FF FE UTF-16/UCS-2, little endian
FE FF UTF-16/UCS-2, big endian
FF FE 00 00 UTF-32/UCS-4, little endian.
00 00 FE FF UTF-32/UCS-4, big-endian.
當你新建一個文本文件時,記事本的編碼默認是ANSI(代表系統默認編碼,在中文系統中一般是GB系列編碼), 如果你在ANSI的編碼輸入漢字,那麼他實際就是GB系列的編碼方式,在這種編碼下,"聯通"的內碼是:
c1 1100 0001
aa 1010 1010
cd 1100 1101
a8 1010 1000
注意到了嗎?第一二個位元組、第三四個位元組的起始部分的都是"110"和"10",正好與UTF8規則里的兩位元組模板是一致的,
於是當我們再次打開記事本時,記事本就誤認為這是一個UTF8編碼的文件,根據Unicode和UTF-8轉換的規則,讓我們把第一個位元組的110和第二個位元組的10去掉,我們就得到了"00001 101010",再把各位對齊,補上前導的0,就得到了"0000 0000 0110 1010",不好意思,這是UNICODE的006A,也就是小寫的字母"j",而之後的兩位元組用UTF8解碼之後是0368,這個字元什麼也不是。這就是只有"聯通"兩個字的文件沒有辦法在記事本里正常顯示的原因。
而如果你在"聯通"之後多輸入幾個字,其他的字的編碼不見得又恰好是110和10開始的位元組,這樣再次打開時,記事本就不會堅持這是一個utf8編碼的文件,而會用ANSI的方式解讀之,這時亂碼又不出現了。
可以根據統計規律(頻率、Markov 鏈)猜測,但是不能確定。
ANSI 編碼本身就是偽概念,應該說 locale 的本地編碼比較好...
總之理論上完全靠譜只能通過編碼的合法與非法情況來猜,還好 UTF-8 的非法情況比較多(
還不如統一上 UTF-8 呢(逃在不清楚編碼的情況下,默認使用utf8,但發現utf8無法解析的時候,使用本地編碼。windows記事本是這樣處理的,你可以參考
試。。
用的比較多的是 chardet/uchardet 猜編碼
沒人答自己回了,嚴格區分是不可能的,但是在數據量比較大的情況下還是可以分辨出無bom的utf8的,按照它的編碼規則一個個檢查過去就行了。
http://lmgtfy.com/?q=uchardet
一般文本編輯器是不會用馬爾可夫鏈之類的東西檢測整個文件的,因為一旦文件很大,效率太低。但是程序會去用一些編碼試圖匹配前若干個位元組,所以如果一個UTF-8的中文文檔恰好前幾段都是英文,也會被識別成ASCII 。所以才需要BOM嘛。
推薦閱讀:
※十進位轉二進位為什麼不是除以2?
※請大神幫忙看看這個編碼是什麼意思?十分感謝
※計算機對比DNA、指紋、人臉等非編碼類數據原理?
※為什麼在莫爾斯碼中不採取和布萊葉盲文一樣的換擋碼錶示切換英文和數字?
TAG:編碼 |