web-dev-qa-db-ja.com

UnicodeDecodeError in Pythonファイルを読み取るときに、エラーを無視して次の行にジャンプする方法は?

テキストファイルをPythonに読み込む必要があります。ファイルのエンコードは次のとおりです。

file -bi test.csv 
text/plain; charset=us-ascii

これはサードパーティのファイルであり、毎日新しいファイルを取得するため、変更することは避けたいです。ファイルには、たとえばÖなどの非ASCII文字が含まれています。 pythonを使用して行を読む必要があり、非ASCII文字を含む行を無視する余裕があります。

私の問題は、Pythonでファイルを読み取ると、非ASCII文字が存在する行に到達するとUnicodeDecodeErrorが発生し、残りのファイルを読み取れないことです。

これを回避する方法はありますか。これを試してみると:

fileHandle = codecs.open("test.csv", encoding='utf-8');
try:
    for line in companiesFile:
        print(line, end="");
except UnicodeDecodeError:
    pass;

次に、エラーに達するとforループが終了し、ファイルの残りを読み取ることができません。間違いの原因となっている行をスキップして続行します。可能であれば、入力ファイルに変更を加えません。

これを行う方法はありますか?どうもありがとうございました。

17
Chicoscience

ファイルはUTF-8エンコードを使用していないようです。ファイルを開くときに正しいコーデックを使用することが重要です。

Youcantell open()errorsキーワードを使用したデコードエラーの処理方法:

errorsは、エンコードおよびデコードエラーの処理方法を指定するオプションの文字列です。これはバイナリモードでは使用できません。 codecs.register_error()に登録されているエラー処理名も有効ですが、さまざまな標準エラーハンドラが利用できます。標準名は次のとおりです。

  • 'strict'は、エンコードエラーがある場合にValueError例外を発生させます。 Noneのデフォルト値は同じ効果があります。
  • 'ignore'はエラーを無視します。エンコードエラーを無視すると、データが失われる可能性があることに注意してください。
  • 'replace'は、不正なデータがある場所に置換マーカー(「?」など)を挿入します。
  • 'surrogateescape'は、U + DC80からU + DCFFまでの範囲のUnicode Private Use Areaのコードポイントとして、不正なバイトを表します。これらのプライベートコードポイントは、データの書き込み時にsurrogateescapeエラーハンドラーが使用されると、同じバイトに戻されます。これは、未知のエンコーディングのファイルを処理するのに役立ちます。
  • 'xmlcharrefreplace'は、ファイルへの書き込み時にのみサポートされます。エンコードでサポートされていない文字は、適切なXML文字参照&#nnn;に置き換えられます。
  • 'backslashreplace'(書き込み時のみサポート)は、サポートされていない文字をPythonのバックスラッシュ付きエスケープシーケンスに置き換えます。

'strict''ignore''replace'など)以外でファイルを開くと、例外が発生することなくファイルを読み取ることができます。

デコードは、テキスト行ごとではなく、バッファリングされたデータブロックごとに行われることに注意してください。行ごとにエラーを検出する必要がある場合は、surrogateescapeハンドラーを使用して、サロゲート範囲内のコードポイントの各行の読み取りをテストします。

import re

_surrogates = re.compile(r"[\uDC80-\uDCFF]")

def detect_decoding_errors_line(l, _s=_surrogates.finditer):
    """Return decoding errors in a line of text

    Works with text lines decoded with the surrogateescape
    error handler.

    Returns a list of (pos, byte) tuples

    """
    # DC80 - DCFF encode bad bytes 80-FF
    return [(m.start(), bytes([ord(m.group()) - 0xDC00]))
            for m in _s(l)]

例えば。

with open("test.csv", encoding="utf8", errors="surrogateescape") as f:
    for i, line in enumerate(f, 1):
        errors = detect_decoding_errors_line(line)
        if errors:
            print(f"Found errors on line {i}:")
            for (col, b) in errors:
                print(f" {col + 1:2d}: {b[0]:02x}")

すべてのデコードエラーが正常に回復できるわけではないことを考慮してください。 UTF-8は小さなエラーに直面しても堅牢になるように設計されていますが、UTF-16やUTF-32などの他のマルチバイトエンコーディングは、ドロップまたは余分なバイトに対処できません。あります。上記の方法では、ファイルの残りの部分が1つの長い行として扱われます。ファイルが十分に大きい場合、「行」が十分に大きい場合、それはMemoryError例外につながる可能性があります。

45
Martijn Pieters