python小記——python的編碼問題詳解

python文件IO操作時,對編碼知識一概不知,那可真的是蛋疼了哈。utf-8啊、gbk、還有encode()、decode()等函數以及encoding參數,搞不好就亂碼甚至是報錯,那可是頭都大了。不說了不說了先上代碼先上代碼。(下面代碼是在Windows系統上進行演示的)

import sys
import locale
str = '小甲'
print(str)
print(type(str))
print(sys.getdefaultencoding())
print(locale.getdefaultlocale())
with open('utf','w',encoding='utf-8') as file:
    file.write(str)
with open('gbk','w',encoding='gbk') as file:
    file.write(str)

輸出結果:

小甲
<class 'str'>
utf-8
('zh_CN', 'cp936')

解釋:
getdefaultencoding():返回的是系統編碼格式,這裏的系統指的不是操作系統,而是python編譯器的默認編碼,python3的編碼是UTF-8。
getdefaultlocale():返回的是本地編碼格式,這裏指的是電腦的操作系統的編碼格式,Windows是GBK,Linux是UTF-8.
而上面顯示的打印結果(‘zh_CN’, ‘cp936’)中,CP936其實就是GBK,IBM在發明Code Page的時候將GBK放在第936頁,所以叫CP936。GBK和UTF-8簡單的來說,區別就是編碼方式不同,表示的文字範圍不同。(UTF-8能表示更多的語言文字,更加通用)。

把上面輸入了內容的兩個文件打開並查看內容,如下;

with open('utf','r') as file:
    print(file.read())

with open('gbk','r') as file:
    print(file.read())

執行結果:

灝忕敳
小甲

到這裏,有沒有懵了呢?是不是有這麼個有疑問:明明在兩個文件裏寫入了相同的字符,查看的結果怎麼會不一樣呢?
其實上面兩個文件寫入的內容分別指定了編碼格式:就是用encoding這個參數作爲指定的編碼格式。
打開第一個文件“utf”時指定的編碼格式是“utf-8”,打開第二個文件“utf”時指定的編碼格式是“gbk”;而此時打開文件的open函數並沒有用encoding參數指定編碼格式,所以就引發了上面輸出不一樣的結果的問題。下面說兩個重點:
系統默認編碼:在python3編譯器讀取.py文件時,若沒有頭文件編碼聲明,則默認使用系統默認編碼“utf-8”來對.py文件進行解碼。並且在調用 encode()這個函數時,不傳參的話默認是“ utf-8 ”。
本地默認編碼:在你編寫的python3程序時,若使用了 open( )函數 ,而不給它傳入 “ encoding ” 這個參數,那麼會自動使用本地默認編碼。沒錯,如果在Windows系統中,就是默認用gbk格式。
由此,就可以知道上面出現結果不一樣的原因了:**開始時指定了不一樣的編碼格式進行寫入內容,而後面都使用了默認的GBK編碼格式以至於出現了不一樣的結果了。要知道,編碼是 “編” 和 “解” 的兩個步驟,一定要一一對應才能正確解碼!雖然通常我們都叫“編碼格式”,這是有一定誤導性的。實際上另一半是“解碼格式”,要有意識地區分 “編” 和 “解” ,我們不能像網上有些文章一樣將這兩者混爲一談。**所以上面的第一個文件開始時用“encoding=utf-8”指定編碼格式寫入內容,而用默認的“GBK”格式進行解碼並輸出,編與解並沒有一一對應即出現了亂碼的情況。

with open('utf','r',encoding="utf-8") as file:
    print(file.read())

with open('gbk','r',encoding="gbk") as file:
    print(file.read())

輸出結果:

小甲
小甲

這樣做到編與解兩個步驟一一對應對能正常打印出來。
到這裏了,是否有了一定的瞭解了呢?這裏只是我們全程進行指定編碼格式地去寫入數據和讀取數據,但是我現在要求你讀取一個文件的數據可不知道文件寫入數據時的編碼格式怎麼辦呢?重點在後面啦。
判斷文件的編碼格式
方便檢閱,我們這裏就用上面的兩個文件啦。
安裝: pip install chardet
如果是使用Pycharm可以直接在Pycharm裏安裝

import chardet
with open('utf','rb') as file: #必須用二進制的方式進行讀取,否則會報錯,因爲chardet的探測器是用字節作爲標本以給探測器進行檢測的
    w = file.read()
    info = chardet.detect(w)
    print(info)

with open('gbk','rb') as file1:
    w = file1.read()
    info = chardet.detect(w)
    print(info)

輸出結果:

{'encoding': 'utf-8', 'confidence': 0.7525, 'language': ''}
{'encoding': 'ISO-8859-1', 'confidence': 0.73, 'language': ''}

到這裏,是否又有疑惑了,第二個文件的編碼格式與我們實際上的不一樣了,可這是爲什麼呢?首先我們先了解一下什麼是ISO-8859-1。
維基百科解釋得非常好:UTF-8與Latin-1(ISO-8859-1)。前者是可變長度編碼,後者是單字節固定長度編碼。Latin-1僅編碼Unicode字符集的前256個代碼點,而UTF-8可用於編碼所有代碼點。ISO-8859-1 and windows-1252 (Western European languages 西歐文字)
哈哈哈,看完了,沒錯,和你想的完全一樣,這個ISO-8859-1和我們實際上的GBK完全不一樣。其實是這樣的…
閱讀官方文檔,**當字節包的長度不夠長時,chardet給出的結論是很不可靠的,因爲它可能在調用一個不相干的探測器時,該探測器給出了一個超過閾值的可信度,或者兩個編碼格式剛好有共通的字符,然後就不再往下檢測。這麼做很容易出現不可靠的檢測結果。**所以最好不要檢測很少量的字節。同時,在檢測開篇有大段其他字符的文檔時,最好先手動處理掉無關的符號(不一定會發生錯誤,因爲程序根據初次遍歷結果對探測器的順序做了優先級排序,但是保不準會出錯),以求最準確的結果。
爲了檢測這個chardet模塊的真實性,手動改了gbk文件裏的內容再次進行上面代碼執行並檢測。手動改動的內容如下:
在這裏插入圖片描述
執行結果:
在這裏插入圖片描述
從結果可以看出,第二文件的編碼格式和我們實際上的一致了。結果裏的那個“confidence”可以理解爲可信度的意思。
所以,這個方法是可以把文件的編碼格式判斷出來的,就是有時相對於比較少字節內容時就會顯得不是很靠譜了。

若有不足之處望留言!

——————END———————
Programmer:柘月十七

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章