UnicodeDecodeError в Python при чтении файла, как игнорировать ошибку и перейти к следующей строке?

Мне нужно прочитать текстовый файл в Python. Кодировка файла:

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

Это сторонний файл, и каждый день я получаю новый, поэтому я не хочу его менять. Файл имеет не ascii-символы, например,.... Мне нужно прочитать строки, используя python, и я могу позволить себе игнорировать строку с символом, отличным от ascii.

Моя проблема в том, что когда я читаю файл на Python, я получаю UnicodeDecodeError при достижении строки, где существует символ без ascii, и я не могу прочитать остальную часть файла.

Есть ли способ избежать этого. Если я попробую это:

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

то, когда ошибка достигнута, цикл for заканчивается, и я не могу прочитать оставшийся файл. Я хочу пропустить линию, которая вызывает ошибку, и продолжайте. Я бы предпочел не делать никаких изменений во входном файле, если это возможно.

Есть ли способ сделать это? Большое вам спасибо.

Ответ 1

Ваш файл, по-видимому, не использует кодировку UTF-8. При открытии файла важно использовать правильный кодек.

open() вы можете рассказать, как обрабатывать ошибки декодирования, с помощью ключевого слова errors:

error является необязательной строкой, которая определяет, как должны обрабатываться ошибки кодирования и декодирования - это нельзя использовать в двоичном режиме. Доступно множество стандартных обработчиков ошибок, хотя любое имя обработки ошибок, зарегистрированное с помощью codecs.register_error(), также допустимо. Стандартные имена:

  • 'strict', чтобы вызвать исключение ValueError, если есть ошибка кодирования. Значение по умолчанию None имеет тот же эффект.
  • 'ignore' игнорирует ошибки. Обратите внимание, что игнорирование ошибок кодирования может привести к потере данных.
  • 'replace' вызывает вставку замещающего маркера (например, "?") В случае искаженных данных.
  • 'surrogateescape' будет представлять любые неправильные байты как кодовые точки в Частной области использования Юникода в диапазоне от U + DC80 до U + DCFF. Эти частные кодовые точки будут затем возвращаться в те же байты, когда при записи данных используется обработчик ошибок surrogateescape. Это полезно для обработки файлов в неизвестной кодировке.
  • 'xmlcharrefreplace' поддерживается только при записи в файл. Символы, не поддерживаемые кодировкой, заменяются соответствующей ссылкой на символ XML &#nnn;.
  • 'backslashreplace' (также поддерживается только при записи) заменяет неподдерживаемые символы на escape-последовательности с обратной косой чертой Pythons.

Открытие файла с помощью чего-либо, кроме '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, не могут справиться с отброшенными или лишними байтами, что затем повлияет на точность разделителей строк. расположены. Приведенный выше подход может привести к тому, что остальная часть файла будет обрабатываться как одна длинная строка. Если файл достаточно велик, это может, в свою очередь, привести к исключению MemoryError, если "строка" достаточно велика.