之前一直在糾結這些格式到底有什麼區別,有時候因爲格式的問題會讓人抓狂。
下面通過實戰來分析下:
下面在windows上建立一個txt文檔。txt的優勢是沒有文件頭,這樣比較好分析。
ANSI格式:
可以看到,0D0A是\r\n,可以忽略,這裏的蛤被識別成B8 F2
UTF-8無BOM:
這裏的英文字母還是ASCII格式,漢字在這裏被識別成 E8 9B A4
UTF-8:
英文字母是ASCII,這裏前面多了三個字節: EF BB BF,這個東西可能是BOM
蛤被識別成E8 9B A4
UCS2 大端:
這裏有一個前綴 FE FF
這裏的0123對應的30,31,32被擴展成兩個字節,\r\n也是。
漢字被識別成 86 E4
可以注意到,對於0,識別出 0030,是大端排列的
UCS2小端:
這裏有一個前綴 FF FE
另外一個區別是數字和漢字對應的編碼是小端排列的
那麼這裏來總結一下:
1.除了UCS2,其他格式的ASCII字母都是一個字節。
2.UCS2和UTF8都帶有前綴
3.對於ANSI和UCS2,漢字被識別成2個字節,對於UTF8,漢字被識別成三個字節(實際上UTF8中,應該隨着漢字而變化)。
這裏的UCS2不論什麼都識別成兩個字節,比較變態。
網絡上建議爲了兼容性,使用無BOM的UTF8
那麼有沒有BOM有什麼區別呢?知乎上這麼解答(http://www.zhihu.com/question/20167122):
下面的粗體字都是從這裏拷過來的:
UTF-8 不需要 BOM,儘管 Unicode 標準允許在 UTF-8 中使用 BOM。所以不含 BOM 的 UTF-8 纔是標準形式
後面又提到:在 UTF-8 文件中放置 BOM 主要是微軟的習慣。 這樣就釋懷了,原來是微軟乾的,好像系統自帶的記事本廣受吐槽。
微軟在 UTF-8 中使用 BOM 是因爲這樣可以把 UTF-8 和 ASCII 等編碼明確區分開,但這樣的文件在 Windows 之外的操作系統裏會帶來問題。
「UTF-8」和「帶 BOM 的 UTF-8」的區別就是有沒有 BOM。即文件開頭有沒有 U+FEFF。
在網頁上使用BOM是個錯誤。BOM設計出來不是用來支持HTML和XML的。要識別文本編碼,HTML有charset屬性,XML有encoding屬性,沒必要拉BOM撐場面
其實說BOM是個壞習慣也不盡然,很多shell出於兼容的考慮不檢測BOM,最後回頭想想,似乎也真就只有Windows堅持用BOM了。
看起來BOM(EFBBBF)就是讓編輯器識別他是UTF8,編輯器如果把它識別爲字符的話就會出錯。
UTF8的格式如下:
1、對於單字節符號,字節第一位爲0,後面7位表示字節編碼。
2、對於n字節符號,第一字節的前n位都設爲1,第n+1位爲0,其餘位位編碼位置。
上面的蛤的表示是E8 9B A4,它有三個字節,第一個字節前面是1110,也正好符合。
那麼什麼是UCS2呢?UCS2在windows中指代Unicode。
這裏(http://demon.tw/programming/utf-16-ucs-2.html)有解答:
簡單的說,UTF-16可看成是UCS-2的父集。在沒有輔助平面字符(surrogate code points)前,UTF-16與UCS-2所指的是同一的意思
所謂微軟的 Unicode,確實是 UTF-16LE,那就是UCS2 LE了。
這裏(http://www.zhihu.com/question/20650946)又講了:
- 所謂的「ANSI」指的是對應當前系統 locale 的遺留(legacy)編碼。[1]
- 所謂的「Unicode」指的是帶有 BOM 的小端序 UTF-16。[2]
- 所謂的「UTF-8」指的是帶 BOM 的 UTF-8。[3]
原來這裏的ANSI實際上是GBK這些編碼啊!
而記事本的ANSI編碼,就是這種默認編碼,所以,一箇中文文本,用ANSI編碼保存,在中文版裏編碼是GBK模式保存的時候,到繁體中文版裏,用BIG5讀取,就全亂套了。
這段話看了之後茅塞頓開啊!沒有文件頭確實難以知道它是什麼編碼。
記事本也不甘心這樣,所以它要支持Unicode,但是有一個問題,一段二進制編碼,如何確定它是GBK還是BIG5還是UTF-16/UTF-8?記事本的做法是在TXT文件的最前面保存一個標籤,如果記事本打開一個TXT,發現這個標籤,就說明是unicode。標籤叫BOM,如果是0xFF 0xFE,是UTF16LE,如果是0xFE 0xFF則UTF16BE,如果是0xEF 0xBB 0xBF,則是UTF-8。如果沒有這三個東西,那麼就是ANSI,使用操作系統的默認語言編碼來解釋。
還有一個簡單的解釋:
Unicode是FFFE後面接着UTF-16字符串的二進制文件,UTF-8是EFBBBF後面接着UTF-8字符串的二進制文件、ANSI是有時候會被解釋爲當前locale對應的code page的字符串的二進制文件。(還是根據前綴來的啊)
看到這一句話,說明GB開頭的也可以無視了:
GB 是國標,中國的一個發佈編碼規範的機構,可以忽略掉,通用性太差了。微軟在 GB2312 的基礎上擴展了 GBK,也可以忽略掉。
那麼UTF8 without BOM怎麼解析呢,沒有文件頭??
一些代碼文件裏面一般是在最前面加入了文件格式的說明,比如HTML的charset,python的開頭一行也有。
這裏有一個回答解決了疑惑(如何自動識別UTF8)
UTF-8 can be auto-detected better by contents than by BOM. The method is simple: try to read the file (or a string) as UTF-8 and if that succeeds, assume that the data is UTF-8. Otherwise assume that it is CP1252 (or some other 8 bit encoding). Any non-UTF-8 eight bit encoding will almost certainly contain sequences that are not permitted by UTF-8. Pure ASCII (7 bit) gets interpreted as UTF-8, but the result is correct that way too.
原來它是智能識別,根據UTF8特定的格式來校驗它是否是UTF8
還有一些問題仍未解決的:
Qt中出現的Latin1,ASCII是什麼?爲什麼Qt經常亂碼?
1.根據百度百科的介紹,Latin1包含ASCII,具有一些其他的字符。當然了,使用英文的時候這些都可以。
2.local8bit應該是本地編碼。QString內部應該要保存UTF8,然後輸入輸出的時候需要轉換一下:
QString str = QString::fromLocal8Bit("漢字");//輸入
std::cout << "Local Output:" << str.local8Bit() << endl;//輸出
根據這篇博客的介紹:
http://blog.csdn.net/brave_heart_lxl/article/details/7186631
可以看出源代碼文件的編碼格式對其也有影響。而且QString賦值的時候必須要告訴QString自己是什麼編碼。
一般給個char*的時候,Qt並不知道你採用的是什麼編碼,而是需要告訴它你採用的是什麼編碼。
正確的操作應該這樣:
告訴Qt 默認的編碼(cpp源文件的編碼需要對應):
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GBK")); QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));設置QString默認採用的編碼。而究竟採用哪一個,一般來說就是源代碼是GBK,就用GBK,源代碼是UTF-8就用UTF-8。
還有涉及到路徑的問題,那就有一個萬能的辦法:
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QTextCodec::setCodecForTr(codec);
QTextCodec::setCodecForLocale(QTextCodec::codecForLocale()); //外部路徑,非文件中的代碼,windows使用的gb2312,這裏就採用了gb2312
QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());//