bug:生产问题,Golang解决csv文件用excel打开中文乱码问题及常见编码和BOM头关系
bug:Golang解決csv文件用excel打開中文亂碼問題
1 場景及分析
場景:今天在生成csv文件之后,測試發現用office和wps打開亂碼
- 分析:經過測試之后發現使用記事本打開不亂碼,同時用記事本打開之后另存為ANSI編碼之后用office和wps打開之后也不亂碼
- 由此可以斷定應該是生成的csv文件缺少bom頭導致,office和wps無法斷定使用哪種編碼打開文件,最終產生中文亂碼問題
拓展:BOM頭
BOM(Byte Order Mark字節順序標記)是用來判斷文本文件是哪一種Unicode編碼的標記,其本身是一個Unicode字符(“\uFEFF”),位于文本文件頭部。 在不同的Unicode編碼中,對應的bom的二進制字節也不同,因此在文件寫入的時候,我們通常根據BOM頭判斷是哪種編碼
2 解決
由此可以知道,是因為我們的文件沒有BOM頭導致中文亂碼,所以我們對癥下藥,直接寫入BOM頭即可
- writer.Write([]string{“\xEF\xBB\xBF”})
結果:
寫入之后,用十六進制查看
3 拓展:常見編碼和BOM頭
①中文編碼:
- gb2312 (采用兩個字節保存字符漢字,英文數字一個字節)
- GBK (采用兩個字節保存字符漢字,英文數字一個字節)
- GB18030 (英文數字都是一個字節,中文是兩個或四個字節)
- Unicode字符集(包含每個國家的所有字符)國際通用,unicode編碼 使用兩個字節—65536個字符,浪費空間為了節省空間使用轉碼形式
- utf-8 使用 1 、2、3個字節 (EF BB BF 記事本添加的BOM(Byte Order Mark)頭,編碼的標記)
- utf-16 使用兩個字節—65536個字符 (FF FE 小端(尾) FE FF 大端(尾))
- utf-32 使用4個字節
- 臺灣 big5
- ANSI:在簡體中文Windows操作系統中, ANSI 編碼代表 GBK 編碼
②BOM頭(記事本特有的)BOM頭: Byte Order Mark
- 標識文件的編碼,實際大小比數據多3個字節
- 直接在記事本編輯數據保存,默認會給你的數據添加上BOM頭,使你的文件的大小比實際數據多3個字節(utf-8編碼)。但是,當你使用java程序往記事本寫入數據的時候,不會添加BOM頭
- 例如:當你用utf-8的格式編碼的時候,用程序去讀取文件,雖然顯示的數據是文件中保存的數據,但是,可以用EditPlus打開程序編譯后的.class文件,并且轉化為16進制展示,你就會發現,在前面的3個字節會是 :EF BB BF 這三個字節告訴記事本,這是一個用utf-8編碼的文件。
③分類
- utf-8 EF BB BF
- utf-16(Unicode) FF FE 編碼的時候,小的在后面(FE在后面) 小端 little endian
- utf-16(Unicode big endian) FE FF 編碼的時候,大的在后面(FF在后面) 大端 little endian
我用Notepad2新建個文本,寫上2個字: 我a
1.先轉成ANSI編碼:用Hex WorkShop打開 CE D2 61 (我:CE D2 , a:61H)
2.轉成Unicode編碼:(little-endian) FF FE 11 62 61 00 (我:6211H , a:0061H)
3.轉成Unicode編碼:(big-endian) FE FF 62 11 00 61
4.轉成UTF-8編碼: E6 88 91 61 (我:E68891H , a:61H)
5.轉成UTF-8編碼:(帶BOM) EF BB BF E6 88 91 61 (就多了個EF BB BF頭)
3.1 ANSI
(American National Standards Institute,美國國家標準學會)
ANSI編碼標準是指所有從基本ASCII碼基礎上發展起來的編碼標準,
比如擴展的ASCII碼(128~255占用)、GB2312、GBK、GB18030、BIG5等。每種編碼在ANSI標準中都為一頁,
比如encoding.gb2312頁代表GB2312字符集編碼
3.2 ASCII
(American Standard Code for Information Interchange,美國信息交換標準碼)碼
ANSI的ASCII字符集占一個字節 ,8個位
起始占用: 0x00-0x7f(127個字符狀態) ,半角
擴充后全部占用: 0x00-0xff(共256個字符)
3.3 GB2312
常說的全角,使用2個字節編碼,共收錄了7445個字符,包括6763個漢字和682個其它符號
小于127的字符意義與原來相同,
當兩個大于127的字節連在一起,就表示一個漢字,
前面的一個字節(高字節)從0xA1-0xF7,后面一個字節(低字節)從0xA1-0xFE。
GB2312的兩個字節的最高位都是1,符合這個條件的碼位只有128*128=16384個
3.4 GBK
不再要求低字節一定小于127,只要第一個字節大于127,就認為是一個漢字的開始,
不管后面的字節是否小于127,都要和第一個字節組成一個兩字節的漢字.
GBK包含了GB2312的所有內容,同時又增加了近20000個新的漢子(包括繁體字)和符號
3.5 BG18030
就是GBK的升級版,增加了很多字符,
中文Windows的缺省內碼還是GBK,因為GB18030相對GBK增加的字符,
普通人是很難用到的
BG18030每個字可以由1個、2個或4個字節組成
單字節:其值從0到0x7F。
雙字節:第一個字節的值從0x81到0xFE,第二個字節的值從0x40到0xFE(不包括0x7F)
四字節:第一個字節的值從0x81到0xFE,第二個字節的值從0x30到0x39,第三個字節從0x81到0xFE,第四個字節從0x30到0x39。
3.6 BIG5
是香港、臺灣繁體中文區的字符集編碼標準。由于是各自獨立完成編碼標準,所以最后互相不兼容。
從ASCII、GB2312、GBK到GB18030,,這些編碼方法是向前兼容的,即同一個字符在這些方案
中總是有相同的編碼,區分中文編碼的方法是高字節的最高位不為0。按照程序員的稱呼,
GB2312、GBK到GB18030和BIG5都屬于DBCS(double-byte charater set,雙字節字符集)
或者說MBCS(mutil-byte charater set,多字節字符集)
在DBCS雙字節字符集中,GB內碼的存儲格式始終是big endian,即高位在前。
在讀取DBCS字符流時,只要遇到高位為1的字節,就可以將下兩個字節作為一個雙字節編碼,
而不用管低字節的高位是什么。
3.7 Unicode
Unicode的學名是"Universal Multiple-Octet Coded Character Set",簡稱為UCS。
UCS可以看作是"Unicode Character Set"的縮寫。
ISO(International Organization for Standardization或International Standard Organized)國際標準化組織
廢除了所有地區性編碼方案,重新搞了一套可以包含地球上所有文化的文字和符號的編碼方案。
他們稱這個方案為Universal Multiple-Octet Coded Character Set(通用多8位編碼字符集),簡稱UCS
ISO直接規定必須用兩個字節,也就是16位來統一表示所有的字符,對于ASCII里的那些“半角”字符,
UNICODE保持其原碼不變,只是將其由原來的8位擴展為16位,而其它文化和語言的字符則全部重新統一編碼。
由于“半角”英文符號只用到了低8位,所以其高8位永遠是0,會多浪費一倍的空間.
由于UNICODE設計初期的局限性(并沒有考慮到與現有編碼的兼容性),
所以使得UNICODE與GBK(GB18030、BG2312等)在排版上完全不一樣,
沒有一種簡單的算法可以把內容從UNICODE編碼和兩一種編碼進行轉換,這種轉換必須通過查表來進行。
Unicode是2個字節的編碼,所以也稱UCS-2,如果幾百年后地球上的字符又多了很多的話,ISO已經準備好了UCS-4方案了
也就是4個字節的編碼,而Unicode只與ASCII兼容(更準確地說,是與ISO-8859-1兼容),與GB碼不兼容。
例如“漢”字的Unicode編碼是6C49,而GB碼是BABA。
在非 Unicode 環境下,由于不同國家和地區采用的字符集不一致,很可能出現無法正常顯示所有字符的情況。
微軟公司使用了代碼頁(Codepage)轉換表的技術來過渡性的部分解決這一問題,
即通過指定的轉換表將非 Unicode 的字符編碼轉換為同一字符對應的系統內部使用的 Unicode 編碼。
可以在“語言與區域設置”中選擇一個代碼頁作為非Unicode編碼所采用的默認編碼方式,
如936為簡體中文GBK,950為正體中文Big5(皆指PC上使用的)。在這種情況下,
一些非英語的歐洲語言編寫的軟件和文檔很可能出現亂碼。而將代碼頁設置為相應語言中文處理又會出現問題,
這一情況無法避免。從根本上說,完全采用統一編碼才是解決之道,但目前尚無法做到這一點。
代碼頁技術現在廣泛為各種平臺所采用。UTF-7(的代碼頁是65000,UTF-8 的代碼頁是65001。
3.8 UTF-8
任何文字在Unicode中都對應一個值,這個值稱為代碼點code point.代碼點的值通常寫成U+ABCD的格式
而文字和代碼點之間的對應關系就是UCS-2(Universal Character Set coded in 2 octets)
UCS-4,即用四個字節表示代碼點。
它的范圍為 U+00000000~U+7FFFFFFF,其中 U+00000000~U+0000FFFF和UCS-2是一樣的。
UCS-2和UCS-4只規定了代碼點和文字之間的對應關系,并沒有規定代碼點在計算機中如何存儲。
規定存儲方式的稱為UTF(Unicode Transformation Format),其中應用較多的就是UTF-16和UTF-8了
UTF是“UCS Transformation Format”的縮寫,
是"Unicode字符集轉換格式",是"怎么樣將Unicode定義的數字轉換成程序數據"
UTF-8以字節為單位對Unicode進行的特殊編碼。從Unicode到UTF-8的編碼方式如下:
Unicode編碼(16進制) ║ UTF-8 字節流(二進制)
000000 - 00007F ║ 0xxxxxxx
000080 - 0007FF ║ 110xxxxx 10xxxxxx
000800 - 00FFFF ║ 1110xxxx 10xxxxxx 10xxxxxx
010000 - 10FFFF ║ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
UTF-8的特點是以字節為單位對Unicode進行編碼,對不同范圍的字符使用不同長度的編碼。
對于0x00-0x7F之間的字符,UTF-8編碼與ASCII編碼完全相同。UTF-8編碼的最大長度是4個字節。
從上表可以看出,4字節模板有21個x,即可以容納21位二進制數字。Unicode的最大碼位0x10FFFF也只有21位。
例1:“漢”字的Unicode編碼是0x6C49。0x6C49在0x0800-0xFFFF之間,使用3字節模板了 1110xxxx 10xxxxxx 10xxxxxx
將0x6C49寫成二進制是:0110 1100 0100 1001, 用這個比特流依次代替模板中的x,
得到:11100110 10110001 10001001,即E6 B1 89。
例2:Unicode編碼0x20C30在0x010000-0x10FFFF之間,使用用4字節模板了:
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。
將0x20C30寫成21位二進制數字(不足21位就在前面補0):0 0010 0000 1100 0011 0000,
用這個比特流依次代替模板中的x,
得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。
UTF-8是ASCII的一個超集。因為一個純ASCII字符串也是一個合法的UTF-8字符串,所以現存的ASCII文本不需要轉換。
為傳統的擴展ASCII字符集設計的軟件通常可以不經修改或很少修改就能與UTF-8一起使用。
使用標準的面向字節的排序例程對UTF-8排序將產生與基于Unicode代碼點排序相同的結果。
(盡管這只有有限的有用性,因為在任何特定語言或文化下都不太可能有仍可接受的文字排列順序。)
UTF-8和UTF-16都是可擴展標記語言文檔的標準編碼。所有其它編碼都必須通過顯式或文本聲明來指定。
任何面向字節的字符串搜索算法都可以用于UTF-8的數據(只要輸入僅由完整的UTF-8字符組成)。
但是,對于包含字符記數的正則表達式或其它結構必須小心。
3.9 UTF-16、UTF-32
- UTF-16編碼以16位無符號整數為單位,詳見百度google
- UTF-32編碼以32位無符號整數為單位,詳見百度google
字節序和BOM
① 字節序
PowerPC系列采用big endian方式存儲數據,
而x86系列則采用little endian方式存儲數據,
比如:0x12345678 雙字型數據 ,占4個字節
低位數據----------------->高位數據
12 34 56 78 H
低地址------------------->高地址
0x01 0x02 0x03 0x04 內存中
| 12 | 34 | 56 | 78 | big endian 方式
| 78 | 56 | 34 | 12 | little endian方式
little endian方式個人理解:
(起始地址存放高位數據,左邊12是低數據位放在尾部,是低數據位,不是指二進制中的右邊的低數值位)
C/C++語言編寫的程序里數據存儲順序是跟編譯平臺所在的CPU相關的,
而java是跨平臺的,采用big endian方式來存儲數據
網絡字節序也是big endian方式
②BOM
BOM(byte-order mark)文件編碼頭,即 字節順序標記.
它是插入到以UTF-8、UTF16或UTF-32編碼文件開頭的特殊標記,
用來標記多字節編碼文件的編碼類型和字節順序(big-endian或little- endian)。
一般用來識別文件的編碼類型。
根據字節序的不同,UTF-16可以被實現為UTF-16LE或UTF-16BE,UTF-32可以被實現為UTF-32LE或UTF-32BE。
例如:
Unicode編碼 ║ UTF-16LE ║ UTF-16BE ║ UTF32-LE ║ UTF32-BE
0x006C49 ║ 49 6C ║ 6C 49 ║ 49 6C 00 00 ║ 00 00 6C 49
0x020C30 ║ 43 D8 30 DC ║ D8 43 DC 30 ║ 30 0C 02 00 ║ 00 02 0C 30
Unicode標準建議用BOM(ByteOrderMark)來區分字節序,
即在傳輸字節流前,先傳輸被作為BOM的字符"零寬無中斷空格"。
這個字符的編碼是FEFF,而反過來的FFFE(UTF-16)和FFFE0000(UTF-32)在Unicode中都是未定義的碼位,
不應該出現在實際傳輸中。
BOM編碼頭 常見形式如下:
EF BB BF = UTF-8 (可選標記,因為Unicode標準未有建議)
FE FF = UTF-16, big-endian (大尾字節序標記)
FF FE = UTF-16, little-endian (小尾字節序標記) (也是windows中的Unicode編碼默認標記)
00 00 FE FF = UTF-32, big-endian (大尾字節序標記)
FF FE 00 00 = UTF-32, little-endian (小尾字節序標記)
對于UTF-8來說,BOM標記的有無并不是必須的,是可選的,因為UTF8字節沒有順序,不需要標記.
也就是說一個UTF-8文件可能有BOM,也可能沒有BOM.
微軟在自己的UTF-8格式的文本文件之前加上了EF BB BF三個字節,
windows上面的notepad等程序就是根據這三個字節來確定一個文本文件是ASCII的還是UTF-8的,
然而這個只是微軟暗自作的標記, 其它平臺上不一定會對UTF-8文本文件做個這樣的標記。
微軟的一些軟件會做這種檢測,但有些軟件不做這種檢測, 而把它當作正常字符處理。(傳說中的亂碼問題)
再舉個例子
說的是Notepad2這個體積小,啟動速度快,功能強的輕量級文本編輯器,代碼高亮等,完全可以替代系統記事本
以前剛用Notepad2的時候,經常在打開一個文本文件時顯示亂碼,點什么編碼轉換也沒用,
比如ViDown.exe維棠下載器程序目錄下的Readme.txt,打開就是亂碼,點擊"文件",“編碼"方式,看到的是Unicode,
ok.先關掉Readme.txt,用16進制編輯器比如Hex WorkShop打開后發現前2個字節是CF C2,這在GBK中的編碼是
下載的"下”,說明該Readme.txt編碼不是Unicode,而是屬于ANSI編碼,
那么避免亂碼就要對Notepad2設置下,點"文件",“編碼’,“默認”,在下拉菜單中找到ANSI936,(上面說過它就是GBK)
并勾上"跳過Unicode檢測”, 好了再打開Readme.txt就正常顯示中文了.
在"文件",“編碼’,下有"UTF-8"和"UTF-8包含簽名”,這2個有什么區別呢?
其中"UTF-8包含簽名",這一選項是將文件編碼格式轉換為UTF-8(包含BOM編碼頭),
翻譯成"包含簽名"就看不懂了…
③關系
參考:https://www.cnblogs.com/saxum/p/15775502.html
總結
以上是生活随笔為你收集整理的bug:生产问题,Golang解决csv文件用excel打开中文乱码问题及常见编码和BOM头关系的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 肘关节附属运动测试软件,肘关节功能锻炼方
- 下一篇: 整懵了,蚂蚁金服6面成功拿下offer涨