python讀取未知編碼文件
背景
在開發日誌分析功能時,需要讀取不同編碼的文件然後對文件內容進行解析,那麼首先要解決的就是如何檢測編碼的問題。
測試文件說明
爲了方便演示,先創建5個測試文件(文件名對應編碼):utf8-file,utf8bom-file,gbk-file,utf16le-file,utf16be-file。5個文件統一寫入以下內容:
abcd
1234
一二三四
使用chardet模塊來檢測編碼
chardet是一個用於編碼檢測的模塊,它可以幫助我們識別一段未知格式的字節是屬於什麼編碼格式。
小文件的編碼檢測
chardet模塊的detect函數接受一個非unicode字符串參數,返回一個字典。該字典包括檢測到的編碼格式和置信度。
>>> import chardet
>>> with open('utf8-file', 'rb') as f:
... result = chardet.detect(f.read())
... print(result)
...
{'encoding': 'utf-8', 'confidence': 0.938125, 'language': ''}
大文件的編碼檢測
考慮到有的文件非常大,如果按照上述方法全部讀入後再判斷編碼格式,效率會變得非常低下,因此使用增量檢測的方式。在這裏我們每次給檢測器傳入一行數據,當檢測器達到最低置信度閾值就可以獲取檢測結果,這樣的話相較於上述方法讀取的內容可能更少,從而可以減少檢測的時間。這個方式的另一個好處就是分塊讀取文件內容,不會就內存造成過大的壓力。
>>> import chardet
>>> from chardet.universaldetector import UniversalDetector
>>> detector = UniversalDetector()
>>> with open('utf8-file', 'rb') as f:
... for line in f:
... detector.feed(line)
... if detector.done:
... break
... detector.close()
... print(detector.result)
...
{'encoding': 'utf-8', 'confidence': 0.938125, 'language': ''}
結合檢測編碼和讀取內容
我們將檢測編碼和讀取文件內容封裝成一個函數,並對5種編碼格式的文件進行了測試。以下代碼在創建UniversalDetector對象時傳入了LanguageFilter.CHINESE參數,這樣可以使檢測結果更加準確。
>>> import io
>>> import chardet
>>> from chardet.universaldetector import UniversalDetector, LanguageFilter
>>> def reading_unknown_encoding_file(filename):
... detector = UniversalDetector(LanguageFilter.CHINESE)
... with open(filename, 'rb') as f:
... for line in f:
... detector.feed(line)
... if detector.done:
... break
... detector.close()
... encoding = detector.result['encoding']
... f = io.TextIOWrapper(f, encoding=encoding)
... f.seek(0)
... for line in f:
... print(repr(line))
...
>>> reading_unknown_encoding_file('utf8-file')
'abcd\n'
'1234\n'
'一二三四'
>>> reading_unknown_encoding_file('utf8bom-file')
'abcd\n'
'1234\n'
'一二三四'
>>> reading_unknown_encoding_file('gbk-file')
'abcd\n'
'1234\n'
'一二三四'
>>> reading_unknown_encoding_file('utf16le-file')
'abcd\n'
'1234\n'
'一二三四'
>>> reading_unknown_encoding_file('utf16be-file')
'abcd\n'
'1234\n'
'一二三四'