Здравствуйте, я работаю с книгой Орейли от Аллена Дауни, чтобы выучить Python3.x. В главе 9 приведен пример работы со списком слов, который находится в файле из проекта Moby.

https://en.wikipedia.org/wiki/Moby_Project

https://web.archive.org/web/20170930060409/http://icon.shef.ac.uk/Moby/

Я прочитал файл german.txt со следующими строками Python.

with open("german.txt") as log:
        for line in log:
                word = line.strip()
                if len(word) > 20:
                        print(word)


Некоторые слова читаются, но наступает перерыв, и я получаю эти строки.

Amtsueberschreitungen
Traceback (most recent call last):
  File "einlesen.py", line 8, in <module>
    for line in log:
  File "/home/alexander/anaconda3/lib/python3.6/codecs.py", line 321, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x82 in position 394: invalid start byte

Какой символ имеется в виду? Как я могу справиться с этим с помощью кода Python.

Спасибо

2
AlexanderCoding 29 Июн 2019 в 17:20

3 ответа

Лучший ответ

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

>>> with open('german.txt', 'rb') as f:
...     bs = f.read()
... 
>>> bs.find(b'\x82')
24970
>>> bs[24960:24980]
b'nebel\rAndr\x82\rAndy\rAne'

Таким образом, байт b '\ x82' является последней буквой в пятибуквенном слове, которое начинается с 'Andr'.

Поиск b '\ x82' на этой странице (пользователь переполнения стека @tripleee), мы можем видеть, какие символы могут соответствовать. Я думаю, что наиболее вероятным совпадением является «é», что дает нам правильное имя «Андре». Перекрестная проверка по списку кодировок Python, наиболее подходящим кодировка cp850, устаревшая кодировка для западноевропейских языков.

Этот код будет читать файл без ошибок:

>>> with open('german.txt', encoding='cp850') as f:
...     for line in f:
...         # do things with line

Если вы обнаружите какие-либо «необычные» символы в данных, возможно, вам придется попробовать альтернативные кодировки. Это связано с тем, что для 8-битных кодировок вполне возможно успешно декодировать байт, но результат не имеет смысла. Например, если мы декодируем из cp1252:

>>> b'Andr\x82'.decode('cp1252')
'Andr‚'
0
snakecharmerb 29 Июн 2019 в 15:39

Согласно документации open():

если кодировка не указана, используемая кодировка зависит от платформы: для получения текущей кодировки локали вызывается locale.getpreferredencoding (False).

То, как файл будет прочитан, у всех разное. Чтобы гарантировать, что файл прочитан правильно, необходимо указать правильную кодировку .

Согласно документации проекта Moby в Википедии, «некоторые символы, не являющиеся ASCII, остаются, представлены с использованием Mac OS Roman кодировки ". В документации модуля Python codecs вы можете найти правильное имя для этого кодека, который является «mac_roman». Таким образом, вы можете использовать следующий код, который не приводит к ошибке декодирования:

with open("german.txt", 'rt', encoding='mac_roman') as log:
    for line in log:
        word = line.strip()
        if len(word) > 20:
            print(word)

< Сильный > UPDATE

Несмотря на документацию, файл, кажется, не кодируется с использованием римской кодировки Mac OS. Я расшифровал файл, используя все возможные кодировки, и сравнил результаты. В списке всего 9 слов, не относящихся к ASCII, и слово «Андре» кажется правильным, как указано в другом ответе. Ниже приведен список возможных кодировок (которые не были ошибочными и включали слово «Андре») и 9 не-ASCII слов, декодированных в соответствии с этой кодировкой:

encodings: cp437, cp860, cp861, cp863, cp865
words: André, Attaché, Château, Conférencier, Cézanne, Fabergé, Lévi-Strauss, Rhônetal, p≥ange

encodings: cp720
words: André, Attaché, Château, Conférencier, Cézanne, Fabergé, Lévi-Strauss, Rhônetal, pٌange

encodings: cp775
words: André, Attaché, Chāteau, Conférencier, Cézanne, Fabergé, Lévi-Strauss, Rhōnetal, p“ange

encodings: cp850, cp858
words: André, Attaché, Château, Conférencier, Cézanne, Fabergé, Lévi-Strauss, Rhônetal, p‗ange

encodings: cp852
words: André, Attaché, Château, Conférencier, Cézanne, Fabergé, Lévi-Strauss, Rhônetal, p˛ange

Для всех вышеупомянутых кодировок первые 8 слов одинаковы при декодировании. Только за последнее слово есть 9 разных результатов.

Основываясь на этих результатах, я думаю , что использовалась кодировка cp720 . Однако я не узнаю последнее слово из списка, поэтому не могу сказать наверняка. Вам решать, какое декодирование наиболее подходит для вас.

1
wovano 29 Июн 2019 в 17:17

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

Обнаружение кодировки с помощью {{ Модуль X0}} дает:

{'encoding': 'Windows-1252', 'confidence': 0.73, 'language': ''}

Что не является кодировкой Python по умолчанию UTF8 -. Чтобы читать файлы с другими кодировками, вам нужно указать желаемую кодировку при чтении файла с помощью параметра encoding= функции open(filename, mode, encoding, ...).

Поскольку кодировка может быть не известна заранее, очень удобно использовать UniversalDetector chardet, чтобы определить кодировку файла и затем передать его в файл, читая так:

from chardet.universaldetector import UniversalDetector

detector = UniversalDetector()
detector.reset()
with open('german.txt', 'rb') as file:
    for line in file:
        detector.feed(line)
        if detector.done:
            break
detector.close()
encoding = detector.result
print(encoding)

with open("german.txt", encoding=encoding) as log:
    for line_num, line in enumerate(log):
        word = line.strip()
        if len(word) > 20:
            print(line_num, word)

Примечание: отлично работает на моем компьютере с немецкими локалями (MacOS 10.10.5 с Python 3.6.2) и выдает ту же ошибку перед обнаружением кодировки, что и OP. Мои локали:

LANG="de_DE.UTF-8"
LC_COLLATE="de_DE.UTF-8"
LC_CTYPE="de_DE.UTF-8"
LC_MESSAGES="de_DE.UTF-8"
LC_MONETARY="de_DE.UTF-8"
LC_NUMERIC="de_DE.UTF-8"
LC_TIME="de_DE.UTF-8"
0
albert 29 Июн 2019 в 14:55