より多くのタイムリーなコンテンツについては、WeChat 公開アカウントにご注目ください: Xiaochuang Youji Machine Learning
バックグラウンド
Python でファイルを読み取ることは日常業務に含まれることが多いですが、次のようなさまざまな失敗がよく発生します。
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xed in position 6342: invalid continuation byte
質問1
分析とトラブルシューティング
この実験では、パンダを使用してテキストを読み取り、最初の 5 つのデータを表示します。
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
この問題は、変換できないバイナリ データが原因で発生します。スクリプトを作成して、全体的な文字セット パラメータの選択に問題があるかどうか、または変換できないバイナリ ブロックが表示されるかどうかを判断できます。
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)))
いくつかの可能性があります:
- 出力コードがすべて 16 進形式である場合は、選択したデコード文字セットが間違っている可能性があります。python2.7 バージョンの場合、インターネット上に次のような対処方法があります。
#coding=utf8
import sys
reload(sys)
sys.setdefaultencoding("UTF-8")
ただし、python3版では上記の方法は廃止されました。
- 文字セットにエラーがある場合は、特定の方法を使用して文字セットのエンコード方法を決定できます。スクリプト コードのこの部分は後で投稿します。notepad++ を使用して対象ファイルを開き、ファイルのエンコーディングを示す右下隅の部分を確認することもできます。
- ファイル全体は正常です。notepad++ で開くと、ファイルが開けることが確認できます。問題はないようですが、Python でデコードするとエラーが発生します。 (これは上記の実験コードの場合です)。
check_pd_read_utf8
関数を実行した結果は次のようになります。
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'
元のファイルを詳しく見てみましょう。
解決
確かに、データの特定の行にコード化された文字セットの一部ではないものがあり、それが「utf-8」デコードの失敗の原因となっています。対処方法は2つありますが、
- 元のデータの対応する行を削除します
- パンダがファイルを読み取るときに、
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
上記の問題は、特定の行に間違った文字があり、そのような間違った文字の存在により、pandas csv パーサーがファイル全体を読み取ることができなくなったことを示しています。
pandas.errors.ParserError: Error tokenizing data. C error: EOF
このエラーが報告されるのは、パンダが csv ファイルを読み取るときに、デフォルトでcsv ファイル内の 2 つの二重引用符の間の内容を文字列に解析し、それをフィールドとして読み取り、2 つの二重引用符の間の区切り文字を無視するためです。したがって、デフォルト モードでは、ファイル内に奇数の二重引用符が現れると、最後の引用符はその引用符が配置されている行から始まり、最後まで単一のフィールドを形成する対応する閉じ引用符は存在しません。ファイルの終わりの文字 (EOF) が文字列に現れると、この例外が報告されます。
元のファイル内の二重引用符の数を数えます。
解決
- 二重引用符の数が偶数になるように、二重引用符の行を直接削除します。
- ファイルを読み取るときにパラメータを追加します
quoting=csv.QUOTE_NONE
より洗練された解決策は、quoting
パラメーターの値を設定して、CSV を読み取るときのパンダの上記のデフォルトの動作を変更することです。pandasread_csv
関数には、この動作に関連する 2 つのパラメータがあります。以下に示すように、pandas の公式ドキュメントから引用したquotechar
参照文字と参照動作です。quoting
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
quote パラメーターは、読み取りおよび解析時に、指定されたシンボルが二重引用符に限定されず、デフォルトで二重引用符として考慮されることを意味します。上で説明したように引用文字として設定された後、引用文字間のコンテンツは、改行と区切り文字を含めて単一のフィールドとして解析され、読み取られます。これは、引用符の動作、つまり引用符の分析をどのように扱うかをquoting
表します。ここには 4 つの状況があり、それらはデフォルトです。これら 4 つのパラメータの説明は次のとおりです。csv.QUOTE_MINIMAL, csv.QUOTE_ALL, csv.QUOTE_NONNUMERIC, csv.QUOTE_NONE
csv.QUOTE_MINIMAL
-
csv.QUOTE_MINIMAL
: 引用文字が見つかった場合にのみ、引用文字間の内容が文字フィールドに解析されて読み込まれます。読み取り後のフィールドには引用文字がありません。つまり、引用文字自体は境界としてのみ使用されます。フィールドは表示されず、書き込み時にファイル内で引用符で囲まれるフィールドのみが引用されます。 -
csv.QUOTE_ALL
: ファイルに書き込むときにすべてのフィールドに引用符を追加します。 -
csv.QUOTE_NONNUMERIC
: ファイルを書き込むときに、数値以外のフィールドに引用符を追加します。 -
csv.QUOTE_NONE
: ファイルを読み取るときは、引用符を解析しないでください。つまり、引用符を通常の文字として扱い、特別な処理をせずに読み込みます。ファイルを書き込むときは、どのフィールドにも引用符を追加しないでください。
quoting
したがって、この実験で発生した異常を解決するには、パラメータを 3 に設定するか、Python の組み込みモジュールの csv をインポートして に設定するだけで済みますcsv.QUOTE_NONE
。そうすれば、パンダは読み取り時に参照文字を通常の文字としてのみ扱うようになります。そのため、ファイルの終わりが見つからなくなるまで、対応する終了参照を探し続けず、エラーが報告されます。もちろん、この行は文字化けしているので、区切り文字の数が異常、つまり区切り後のドメイン数が前の行と一致しない場合はエラーが報告されるので、パラメータを設定するだけで済みます。 False に設定すると、パンダはerror_bad_lines
この種の異常な不良行を自動的に削除し、ファイルの残りの通常の内容を正常に読み取ることができるようにします。on_bad_lines
このパラメータは、on_bad_lines='skip'
同等の機能を実現できるpandas バージョン 1.3 以降で使用することをお勧めします。もちろん、quotechar
関数に従って、他の単一文字quotechar
に設定することもできます。これにより、パンダは二重引用符を通常の文字として扱いますが、そうすることのリスクは、他の引用符によって引き起こされる例外を引き起こす可能性があるため、これはそうではありませんおすすめされた。
付録
次のようにファイルのエンコーディング タイプ コードを確認します。
import chardet
# 使用 chardet 检查文件编码类型
def check_file_encoding_type_chardet(file):
# 二进制方式读取,获取字节数据,检测类型
with open(file, 'rb') as f:
encoding = chardet.detect(f.read())['encoding']
#这种方式把整个文件读取进去,如果存在异常编码异常的字符(比如问题1中的数据),会返回None
#encoding = chardet.detect(f.read()[0:1024])['encoding']# 只读取部分数据,更快
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()
# 检查哪一行出现编码异常
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()