亂碼 python_python教程小實戰,檔案I/O讀寫測試,如何避免亂碼?
技術標籤:亂碼 python
首先,如果看過一篇關於檔案編碼的應該還有點印象。python的str型別是unicode的,在記憶體中是這樣表達的。這裡我先做一個把str內容儲存到檔案中。str內容如下:
test_str='''- ASCII編碼,8bit(一個位元組),能表示的最大的整數就是255(2^8-1=255),而ASCII編碼,佔用0 - 127用來表示大小寫英文字母、數字和一些符號,這個編碼表被稱為ASCII編碼,比如大寫字母A的編碼是65,小寫字母z的編碼是122。 還對一些如'\n','\t','#','@'等字元進行了編碼。- GB2312編碼,16bit(2個位元組),適用於漢字處理、漢字通訊等系統之間的資訊交換,通行於中國大陸;新加坡等地也採用此編碼。中國大陸幾乎所有的中文系統和國際化的軟體都支援GB2312。該標準共收錄6763個漢字,其中一級漢字3755個,二級漢字3008個;同時,GB 2312收錄了包括拉丁字母、希臘字母、日文平假名及片假名字母、俄語西裡爾字母在內的682個全形字元。- GBK編碼,16bit(2個位元組),相容GB2312,收錄了 21003 個漢字,共有 23940個碼位。而且它與 Unicode 組織的Unicode編碼完全相容。- Unicode 編碼,通常16bit(2個位元組),為了統一所有文字的編碼,Unicode應運而生,這是一種所有符號的編碼。從0x000000-0x10FFFF,對應全世界所有的語言、公式、符號。然後把這些數字分成**17 部分**,把常用的放到0x0000-0xFFFF,也就是**2 個位元組,叫做基本平面(BMP)**。從0x010000-0x10FFFF 再劃分為其他平面。unicode只是一種編碼規範,定義了任意一個字元到數字的一一對應關係,不如”漢字”對應的數字是0x6c49和0x5b57。雖然Unicode給所有的字元一一對應的關係,但是如果用來儲存太浪費空間,比如,字元'A',ASCII編碼只需要一個位元組(8位)。但是用Unicode就翻了一倍。網際網路的誕生,需要傳輸的資料非常多,如果都是用Unicode編碼傳輸會造成很大的浪費。UTF-8,UTF-16,UTF-32就誕生。而且最廣泛的就是常用的UTF-8。- UTF-8,Unicode Transformation Format,可變長度編碼,通常使用1~4位元組為每個字元編碼,相容ASCII編碼,這是一種Unicode的一種轉換格式。我把大佬的講解搬過來:'''#建議使用with open()as f:這種方式開啟,避免忘記close引發錯誤。with open('test.txt', 'w', encoding='utf-8') as f: f.write(test_str)with open('test.txt', 'r', encoding='gbk') as f: f.write(test_str)
看下tst.txt檔案內容,儲存成功,那麼再次讀取檔案,檢視檔案編碼會是什麼內容呢?以上的方法都是沒有問題的,往下看
#不指定編碼,讀取模式with open('test.txt', 'r') as f: print(f.encoding)#輸出cp936 with open('test.txt', 'r', encoding='utf-8') as f: print(f.encoding)#輸出utf-8
其實,cp936是GBK,Windows預設使用GBK(CP936)編碼,指定encoding後就改變了。
如果手動複製檔案儲存到txt檔案,再讀取,encoding指定編碼會出現神祕情況呢?
新建一個txt文件,複製內容到檔案中,內容選取:
GB2312,是中國規定的漢字編碼,也可以說是簡體中文的字符集編碼 GBK, 是 GB2312的擴充套件 ,除了相容GB2312外,它還能顯示繁體中文,還有日文的假名 cp936,中文字地系統是Windows中的cmd,預設codepage是CP936,cp936就是指系統裡第936號編碼格式,即GB2312的編碼。(當然有其它編碼格式:cp950 繁體中文、cp932 日語、cp1250 中歐語言。。。) Unicode,是國際組織制定的可以容納世界上所有文字和符號的字元編碼方案。UTF-8、UTF-16、UTF-32都是將數字轉換到程式資料的編碼方案。UTF-8 ,(8-bit Unicode Transformation Format)是最流行的一種對 Unicode 進行傳播和儲存的編碼方式。它用不同的 bytes 來表示每一個程式碼點。ASCII 字元每個只需要用一個 byte ,與 ASCII 的編碼是一樣的。所以說 ASCII 是 UTF-8 的一個子集。
儲存為檔案編碼1.txt。然後通過with open 開啟,不指定encoding。
#不指定編碼,讀取模式with open('test.txt', 'r') as f: print(f.encoding) print(f.read())#輸出,而且能正常讀取。cp936# 指定encoding='utf-8'with open('檔案編碼1.txt', 'r', encoding='utf-8') as f: print(f.encoding) print(f.read())utf-8UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa3 in position 6: invalid start byte
出錯了,能發現檔案的編碼是utf-8,WTF為什麼會這樣????不指定encoding的時候,看了type(f.read())是str型別,也就是unicode。為什麼我指定的讀取方式為encoding後發生錯誤呢?看來不能直接用utf-8編碼讀取檔案。我用以下方式可以正常讀取。
with open('檔案編碼1.txt', 'rb') as f: print(f.read().decode('gbk').encode('utf-8').decode('utf-8'))
先用二進位制讀取檔案,那麼讀取的內容就是bytes,然後用decode()由bytes轉成gbk的str。接著再次轉換。就能正常讀取了。記住下面的方法是用:
encode():由str-->bytes。decode():由bytes --> str。
給檔案追加內容
還是上面的例子,通過,encoding='utf-8'給檔案追加內容試試。內容用中文,比如追加一句話:python是最好的語音!
add_str='python是最好的語音!'with open('檔案編碼1.txt', 'a', encoding='utf-8') as f: f.write(add_str)
亂碼又出現了,真是陰魂不散。如果不指定encoding會怎麼樣?
add_str='python是最好的語音!'with open('檔案編碼1.txt', 'a') as f: f.write(add_str)
經過幾次測試,這樣是沒有問題的,出問題是在utf-8編碼上。連續幾次的追加內容,出亂碼的地方都是utf-8的。正常顯示的都是gbk的內容。
最後,如果遇到檔案亂碼的情況,該怎麼辦?經過我的測試,在windows下,如果是一份新的檔案,你儲存內容的時候,選擇encoding='utf-8-sig'是沒有問題的,但是如果之後要追加內容,就必須一致用utf-8,因為你如果不指定,windows儲存的時候會預設選擇cp936編碼(gbk)。
以上面檔案編碼1.txt為例,現在檔案中存在著GBK和utf-8的編碼方式。如果你讀取的時候,無論你採用utf-8或者gbk讀取,都會發生錯誤。不信可以試試看...
如果我硬要把addstr以utf-8的內容追加有沒有辦法?我目前能想到的就是,檔案1讀取之後,編碼utf-8和要addstr一起儲存到新的檔案中,以utf-8編碼存入。
with open('檔案編碼1.txt', 'r') as f: string = f.read()with open('檔案編碼3.txt', 'w', encoding='utf-8') as fp: fp.write(string.encode('utf-8').decode('utf-8')) fp.write('\n') fp.write('\n') fp.write('\n') fp.write(add_str)
測試結果,沒有亂碼。今天的實戰結束。終於能解決為什麼我的中文出亂碼了。