python讀取文件由于編碼問題失敗匯總以及解決辦法
背景
在日常工作中常常涉及用Python讀取文件,但是經(jīng)常遇到各種失敗,比如:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xed in position 6342: invalid continuation byte
問題1
分析和排查
本次實驗使用 pandas 讀取文本并展示前5條數(shù)據(jù):
import pandas as pd raw_file = "/share/jiepeng.liu/public_data/ner/weiboNER/weiboNER.conll.dev" df = pd.read_csv(raw_file, sep='\t') print(df.head())
讀取文件的時候報錯:
df = pd.read_csv(raw_file, sep='\t') File "/opt/conda/lib/python3.8/site-packages/pandas/util/_decorators.py", line 311, in wrapper return func(*args, **kwargs) File "/opt/conda/lib/python3.8/site-packages/pandas/io/parsers/readers.py", line 680, in read_csv return _read(filepath_or_buffer, kwds) File "/opt/conda/lib/python3.8/site-packages/pandas/io/parsers/readers.py", line 575, in _read parser = TextFileReader(filepath_or_buffer, **kwds) File "/opt/conda/lib/python3.8/site-packages/pandas/io/parsers/readers.py", line 934, in __init__ self._engine = self._make_engine(f, self.engine) File "/opt/conda/lib/python3.8/site-packages/pandas/io/parsers/readers.py", line 1236, in _make_engine return mapping[engine](f, **self.options) File "/opt/conda/lib/python3.8/site-packages/pandas/io/parsers/c_parser_wrapper.py", line 75, in __init__ self._reader = parsers.TextReader(src, **kwds) File "pandas/_libs/parsers.pyx", line 544, in pandas._libs.parsers.TextReader.__cinit__ File "pandas/_libs/parsers.pyx", line 633, in pandas._libs.parsers.TextReader._get_header File "pandas/_libs/parsers.pyx", line 847, in pandas._libs.parsers.TextReader._tokenize_rows File "pandas/_libs/parsers.pyx", line 1952, in pandas._libs.parsers.raise_parser_error UnicodeDecodeError: 'utf-8' codec can't decode byte 0xed in position 6342: invalid continuation byte
該問題是由于出現(xiàn)了無法進行轉(zhuǎn)換的二進制數(shù)據(jù)造成的,可以寫一個腳本來判斷,是整體的字符集參數(shù)選擇上出現(xiàn)了問題,還是出現(xiàn)了部分的無法轉(zhuǎn)換的二進制塊:
raw_file = "/share/jiepeng.liu/public_data/ner/weiboNER/weiboNER.conll.dev" def check_pd_read_utf8(): #以讀入文件為例: f = open(raw_file, "rb")#二進制格式讀文件 print("file_name=", raw_file) line_num = 0 while True: line = f.readline() line_num +=1 if not line: break else: try: #print(line.decode('utf8')) line.decode('utf8') #為了暴露出錯誤,最好此處不print except: print("line num={}, text={}".format(line_num, str(line)))
具體有幾種可能:
- 如果輸出的代碼都是hex形式的,可能就是選擇的解碼字符集出現(xiàn)了錯誤。 對于python2.7版本的來說,網(wǎng)上有使用這樣一種方式處理:
#coding=utf8 import sys reload(sys) sys.setdefaultencoding("UTF-8")
但是,上述這種方法在python3版本中,已經(jīng)取消了。
- 如果是字符集出現(xiàn)錯誤,可以使用特定方式判斷其字符集編碼方式。這部分腳本代碼會在后面補充貼出來。也可以使用notepad++打開目標(biāo)文件,查看右下角的部位,會指示該文件是那種編碼。
- 有的情況,是這樣的,整個文件是好的,如果用notepad++打開后,能夠看到文件是可以打開的,似乎什么問題都沒有發(fā)生過,但是,用python進行解碼的時候,卻會出現(xiàn)錯誤(上述實驗代碼就是這種情況)。
check_pd_read_utf8
函數(shù)運行結(jié)果如下:
line num=996, text=b'\xed\xa0\xbd\tO\n'
line num=997, text=b'\xed\xb0\xad\tO\n'
line num=998, text=b'\xed\xa0\xbd\tO\n'
line num=999, text=b'\xed\xb0\xad\tO\n'
line num=1000, text=b'\xed\xa0\xbd\tO\n'
line num=1001, text=b'\xed\xb0\xad\tO\n'
line num=1875, text=b'\xed\xa0\xbc\tO\n'
line num=1876, text=b'\xed\xbd\x9d\tO\n'
line num=1877, text=b'\xed\xa0\xbc\tO\n'
line num=1878, text=b'\xed\xbd\x9b\tO\n'
line num=1879, text=b'\xed\xa0\xbc\tO\n'
line num=1880, text=b'\xed\xbd\xb1\tO\n'
line num=1881, text=b'\xed\xa0\xbc\tO\n'
line num=1882, text=b'\xed\xbd\xa3\tO\n'
line num=1883, text=b'\xed\xa0\xbc\tO\n'
line num=1884, text=b'\xed\xbd\x99\tO\n'
進一步查看原始文件:
解決方法
確實在特定行數(shù)據(jù)上存在不屬于編碼字符集中的內(nèi)容,從而導(dǎo)致’utf-8’解碼失敗。有兩種處理方式,
- 在原始數(shù)據(jù)中將對應(yīng)的行刪除
- 在pandas讀取文件時,設(shè)置
encoding_errors='ignore'
,將錯誤行直接忽略
import pandas as pd raw_file = "/share/jiepeng.liu/public_data/ner/weiboNER/weiboNER.conll.dev" df = pd.read_csv(raw_file, sep='\t', encoding_errors='ignore') print(df.head())
問題2
分析和排查
在用 pandas.read_csv
讀取文件后報錯:
pandas.errors.ParserError: Error tokenizing data. C error: EOF inside string starting at row 39252
出現(xiàn)上述問題,說明在特定行存在錯誤字符,這種錯誤字符的存在使得 pandas csv 解析器無法讀取整個文件。
pandas.errors.ParserError: Error tokenizing data. C error: EOF
報錯是因為pandas讀取csv文件時,會默認(rèn)把csv文件中兩個雙引號之間的內(nèi)容解析為一個string,作為一個字段域讀入,并且忽略兩個雙引號之間的分隔符。所以,在默認(rèn)方式下,一旦文件中出現(xiàn)了奇數(shù)個雙引號,那么最后一個引號從所在的行開始,直到文件結(jié)束也沒有對應(yīng)的結(jié)束引號形成單個字段域,就會報這個異常,即文件結(jié)束符(EOF)出現(xiàn)在了字符串中。
統(tǒng)計原始文件中雙引號的個數(shù):
解決方法
- 直接刪除一個雙引號的行數(shù)據(jù),從而確保雙引號的數(shù)量為偶數(shù)
- 讀取文件時,增加參數(shù)
quoting=csv.QUOTE_NONE
較為優(yōu)雅的解決方式是設(shè)置參數(shù)quoting
的值,從而改變pandas在讀取csv的上述默認(rèn)行為。在pandas的read_csv
函數(shù)中,有兩個參數(shù)和這個行為有關(guān),分別是quotechar
引用符和quoting
引用行為,如下所示,摘自pandas的官方文檔。
quotechar : str (length 1), optional
The character used to denote the start and end of a quoted item. Quoted items can include the delimiter and it will be ignored.
quoting : int or csv.QUOTE_* instance, default 0
Control field quoting behavior per csv.QUOTE_* constants. Use one of QUOTE_MINIMAL (0), QUOTE_ALL (1), QUOTE_NONNUMERIC (2) or QUOTE_NONE (3).
quotechar
引用符參數(shù)是表示在讀取解析時,將指定的符號認(rèn)為是引用符,不僅僅限制于雙引號,默認(rèn)情況下是雙引號。被設(shè)為引用符之后,就會按照上面所述的那樣,在引用符之間的內(nèi)容會被解析為單個域讀入,包括換行符和分隔符。而quoting
表示引用行為,即如何對待引用符的解析。這里具有四種情況,分別是csv.QUOTE_MINIMAL, csv.QUOTE_ALL, csv.QUOTE_NONNUMERIC, csv.QUOTE_NONE
默認(rèn)是csv.QUOTE_MINIMAL
。這4個參數(shù)的解釋如下:
csv.QUOTE_MINIMAL
:只有當(dāng)遇到引用符時,才會將引用符之間的內(nèi)容解析為一個字符域讀入,并且讀取之后的域是沒有引用符的,即引用符本身只作為一個域的邊界界定,不會顯示出來;在寫入時,也只有具有引用符的域會在文件中加上引用符。csv.QUOTE_ALL
:在寫入文件時,將所有的域都加上引用符。csv.QUOTE_NONNUMERIC
:寫入文件時,將非數(shù)字域加上引用符。csv.QUOTE_NONE
:讀取文件時,不解析引用符,即把引用符當(dāng)做普通字符對待并且讀入,不做特殊的對待;在寫入文件時,也不對任何域加上引用符。
所以,要解決本次實驗過程遇到的異常,只需要將quoting
參數(shù)設(shè)為3,或者導(dǎo)入python的內(nèi)置模塊csv,設(shè)為csv.QUOTE_NONE
,這樣pandas在讀取時,就只會把引用符當(dāng)做普通字符,從而不會一直尋找對應(yīng)的結(jié)束引用符直至文件結(jié)束都沒找到,從而報錯。當(dāng)然,由于這行是亂碼,分隔符數(shù)量很可能也不正常,即分隔后和前面的行的域的個數(shù)不一致,還會報錯,所以只需要將error_bad_lines
參數(shù)設(shè)為False,這樣pandas就會自動刪除這種不正常的bad lines,從而文件剩下的正常的內(nèi)容就可以正常的讀入了。pandas 1.3版本之后推薦使用on_bad_lines
這個參數(shù),可以將其on_bad_lines='skip'
實現(xiàn)等同功能。當(dāng)然,根據(jù)quotechar
的功能,也可以通過將quotechar
設(shè)為其他的單個字符,從而pandas會把雙引號當(dāng)做普通字符,但是這樣做的風(fēng)險在于可能會觸發(fā)其他引用符帶來的異常,所以不推薦這樣做。
附錄
檢查文件編碼類型代碼如下:
import chardet # 使用 chardet 檢查文件編碼類型 def check_file_encoding_type_chardet(file): # 二進制方式讀取,獲取字節(jié)數(shù)據(jù),檢測類型 with open(file, 'rb') as f: encoding = chardet.detect(f.read())['encoding'] #這種方式把整個文件讀取進去,如果存在異常編碼異常的字符(比如問題1中的數(shù)據(jù)),會返回None #encoding = chardet.detect(f.read()[0:1024])['encoding']# 只讀取部分?jǐn)?shù)據(jù),更快 print("chardet check file encoding type=", encoding) file_name = "/share/jiepeng.liu/public_data/ner/weiboNER/weiboNER.conll.train" check_file_encoding_type_chardet(file_name) # 使用 magic 來檢查文件編碼類型 def check_file_encoding_type_magic(): # pip install python-magic import magic blob = open(file_name, 'rb').read() m = magic.Magic(mime_encoding=True) encoding = m.from_buffer(blob) print("magic check file encoding type=", encoding) check_file_encoding_type_magic() # 檢查哪一行出現(xiàn)編碼異常 def check_pd_read_utf8(): #以讀入文件為例: f = open(file_name, "rb")#二進制格式讀文件 print("file_name=", file_name) line_num = 0 while True: line = f.readline() line_num +=1 if not line: break else: try: #print(line.decode('utf8')) line.decode('utf8') #為了暴露出錯誤,最好此處不print except: print("line num={}, text={}".format(line_num, str(line))) check_pd_read_utf8()
總結(jié)
到此這篇關(guān)于python讀取文件由于編碼問題失敗匯總以及解決辦法的文章就介紹到這了,更多相關(guān)python讀取文件編碼問題失敗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python實現(xiàn)requests發(fā)送/上傳多個文件的示例
今天小編就為大家分享一篇python實現(xiàn)requests發(fā)送/上傳多個文件的示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06pytorch/transformers?最后一層不加激活函數(shù)的原因分析
這里給大家解釋一下為什么bert模型最后都不加激活函數(shù),是因為損失函數(shù)選擇的原因,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-01-01Python實現(xiàn)動態(tài)添加屬性和方法操作示例
這篇文章主要介紹了Python實現(xiàn)動態(tài)添加屬性和方法操作,結(jié)合實例形式分析了Python類中屬性與方法的動態(tài)添加、綁定、刪除等相關(guān)操作技巧,需要的朋友可以參考下2018-07-07淺談Python中的可迭代對象、迭代器、For循環(huán)工作機制、生成器
這篇文章主要介紹了Python中的可迭代對象、迭代器、For循環(huán)工作機制、生成器,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03