Python: 轉換文本編碼

  • 2019 年 10 月 3 日
  • 筆記

最近在做周報的時候,需要把csv文本中的數據提取出來製作表格後生產圖表。

在獲取csv文本內容的時候,基本上都是用with open(filename, encoding =’UTF-8′) as f:來打開csv文本,但是實際使用過程中發現有些csv文本並不是utf-8格式,從而導致程序在run的過程中報錯,每次都需要手動去把該文本文件的編碼格式修改成utf-8,再次來run該程序,所以想說:直接在程序中判斷並修改文本編碼。

基本思路:先查找該文本是否是utf-8的編碼,如果不是則修改為utf-8編碼的文本,然後再處理。

python有chardet庫可以查看到文本的encoding信息:

detect函數只需要一個 非unicode字符串參數,返回一個字典(例如:{‘encoding’: ‘utf-8’, ‘confidence’: 0.99})。該字典包括判斷到的編碼格式及判斷的置信度。

import chardet    def get_encode_info(file):      with open(file, 'rb') as f:          return chardet.detect(f.read())['encoding']

 

不過這個在從處理小文件的時候性能還行,如果文本稍微過大就很慢了,目前我本地的csv文件是近200k,就能明顯感覺到速度過慢了,效率低下。不過chardet庫中提供UniversalDetector對象來處理:創建UniversalDetector對象,然後對每個文本塊重複調用其feed方法。如果檢測器達到了最小置信閾值,它就會將detector.done設置為True。一旦您用完了源文本,請調用detector.close(),這將完成一些最後的計算,以防檢測器之前沒有達到其最小置信閾值。結果將是一個字典,其中包含自動檢測的字符編碼和置信度(與charde.test函數返回的相同)。

from chardet.universaldetector import UniversalDetector    def get_encode_info(file):   with open(file, 'rb') as f:          detector = UniversalDetector()   for line in f.readlines():              detector.feed(line)   if detector.done:   break          detector.close()   return detector.result['encoding']

 

在做編碼轉換的時候遇到問題:UnicodeDecodeError: ‘charmap’ codec can’t decode byte 0x90 in position 178365: character maps to <undefined>

def read_file(file):   with open(file, 'rb') as f:   return f.read()    def write_file(content, file):   with open(file, 'wb') as f:          f.write(content)    def convert_encode2utf8(file, original_encode, des_encode):      file_content = read_file(file)      file_decode = file_content.decode(original_encode)   #-->此處有問題      file_encode = file_decode.encode(des_encode)      write_file(file_encode, file)

 

這是由於byte字符組沒解碼好,要加另外一個參數errors。官方文檔中寫道:

bytearray.decode(encoding=”utf-8”, errors=”strict”)

Return a string decoded from the given bytes. Default encoding is ‘utf-8’. errors may be given to set a different error handling scheme. The default for errors is ‘strict’, meaning that encoding errors raise a UnicodeError. Other possible values are ‘ignore’, ‘replace’ and any other name registered via codecs.register_error(), see section Error Handlers. For a list of possible encodings, see section Standard Encodings.

意思就是字符數組解碼成一個utf-8的字符串,可能被設置成不同的處理方案,默認是‘嚴格’的,有可能拋出UnicodeError,可以改成‘ignore’,’replace’就能解決。

所以將此行代碼file_decode = file_content.decode(original_encode)修改成file_decode = file_content.decode(original_encode,’ignore’)即可。

完整代碼:

from chardet.universaldetector import UniversalDetector    def get_encode_info(file):   with open(file, 'rb') as f:       detector = UniversalDetector()       for line in f.readlines():           detector.feed(line)           if detector.done:               break       detector.close()       return detector.result['encoding']    def read_file(file):      with open(file, 'rb') as f:          return f.read()    def write_file(content, file):      with open(file, 'wb') as f:          f.write(content)    def convert_encode2utf8(file, original_encode, des_encode):      file_content = read_file(file)      file_decode = file_content.decode(original_encode,'ignore')      file_encode = file_decode.encode(des_encode)      write_file(file_encode, file)    if __name__ == "__main__":      filename = r'C:UsersdanvyDesktopAutomationtestdatatest.csv'      file_content = read_file(filename)      encode_info = get_encode_info(filename)      if encode_info != 'utf-8':          convert_encode2utf8(filename, encode_info, 'utf-8')      encode_info = get_encode_info(filename)      print(encode_info)

 參考:https://chardet.readthedocs.io/en/latest/usage.html