Запись строки utf-8 в моих файлах python - PullRequest
8 голосов
/ 27 января 2011

Эта строка в моем файле .py дает мне: "UnicodeDecodeError: кодек 'utf8' не может декодировать байты в позиции 8-13: неподдерживаемый диапазон кода Unicode"

if line.startswith(u"Fußnote"):

Файл сохраняется в utf-8 и имеет кодировку вверху: # - - кодировка: utf-8 - -

У меня есть много других файлов py с китайским текстом в кодировке utf-8 в комментариях и массивах, например: arr = [u "китайский текст",], поэтому мне интересно, почему этот случай в частности, не работает для меня.

Ответы [ 5 ]

10 голосов
/ 27 января 2011

Давайте внимательно рассмотрим это сообщение об ошибке:

"UnicodeDecodeError: кодек 'utf8' не может декодировать байты в позиции 8-13: неподдерживаемый диапазон кода Unicode"

Обратите внимание, что оноговорит "байты в позиции 8-13" - это 6-байтовая последовательность UTF-8 .Это могло бы быть допустимо в темные века, но , поскольку Unicode был заморожен на 21 бит, максимум - ЧЕТЫРЕ байта .Проверки UTF-8 и сообщения об ошибках недавно были ужесточены ;Интересно, какую именно версию Python вы используете?

По крайней мере, с 2.7.1 и 2.6.6 эта ошибка становится более полезной "... невозможно декодировать байт XXXX в позиции8: недопустимый начальный байт ", где XXXX может быть только 0xfc или 0xfd, если старое сообщение предлагало 6-байтовую последовательность.В ISO-8859-1 или cp1252 значение 0xfc обозначает малую букву U + 00FC U с диаэрезом (он же u-umlaut, вероятный подозреваемый);0xfd представляет U + 00FD LATIN МАЛЕНЬКОЕ ПИСЬМО Y С ОСТРОМ (менее вероятно).

Проблема НЕ в выражении if line.startswith(u"Fußnote"): в исходном файле.Вы бы получили сообщение во время COMPILE, если бы оно не было надлежащим UTF-8, и сообщение начиналось бы с «SyntaxError», а не «UnicodeDecodeError».В любом случае код UTF-8 для этой строки имеет длину всего 8 байтов, а не 14.

Проблема в том, на что ссылается @Mark Tolonen, в любой «строке», на которую ссылается.Это может быть только объект str.

Чтобы получить дальнейшие сведения, вам нужно ответить на вопросы Марка (1) о результате изменения print repr(line) (2) site.py.

На данном этапе этохорошая идея прояснить вопрос о смешивании объектов str и unicode (во многих операциях, а не только a.startswith(b)).

Если операция не определена для создания объекта str,он НЕ приведёт объект unicode к str. Это не относится к a.startswith(b). Он попытается декодировать объект str, используя кодировку по умолчанию (обычно 'ascii').

Примеры:

>>> "\xff".startswith(u"\xab")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 0: ordinal not in range(128)

>>> u"\xff".startswith("\xab")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xab in position 0: ordinal not in range(128)

Кроме того, Неверно говорить «Смешайте и вы получите UnicodeDecodeError». Вполне возможно, что объект str правильно закодированв кодировке по умолчанию (обычно 'ascii') - исключение не возникает.

Примеры:

>>> "abc".startswith(u"\xff")
False
>>> u"\xff".startswith("abc")
False
>>>
6 голосов
/ 27 января 2011

Я могу воспроизвести UnicodeDecodeError с этим кодом:

#!/usr/bin/env python
# -- coding: utf-8 --

line='Fußnoteno'
if line.startswith(u"Fußnote"):
    print('Hi')

Обратите внимание, что line является строковым объектом, но u"Fußnote" является объектом Unicode. Поскольку line является строковым объектом, объект Unicode преобразуется в строковый объект при вызове startswith. В Python2 по умолчанию используется попытка декодирования с использованием кодека ascii. Поскольку u"ß" не может быть декодирован с помощью кодека ascii, возникает ошибка UnicodeDecodeEr.

Эту ошибку можно избежать, если сначала сделать line объектом Юникода:

line='Fußnoteno'.decode('utf-8')
if line.startswith(u"Fußnote"):
    print('Hi')

или, если вы впервые сделаете u"Fußnote" строковым объектом:

line='Fußnoteno'
if line.startswith(u"Fußnote".encode('utf-8')):
    print('Hi')
3 голосов
/ 27 января 2011

Ошибка указывает, что строка не является строкой Unicode. В X.startswith(Y) оба X и Y должны быть Unicode или байтовой строкой. Смешайте и вы получите UnicodeDecodeError. print repr(line) чтобы проверить это. Также вы изменили site.py, чтобы изменить кодировку по умолчанию с 'ascii' на 'utf8'? Обычно это кодек «ascii», используемый по умолчанию для Python 2.x.

2 голосов
/ 27 января 2011

Не видя ваш код, неясно, является ли проблема кодом или файлом данных, который код читает.

Когда вы открываете файл, вы делаете:

file = open("essay.txt")

или

import codecs
file = codecs.open("essay.txt", encoding="utf-8")

Что значит:

print file.encoding

скажете, если вы добавите его чуть ниже open строки?

Оба эти способа работают для меня:

# -- coding: utf-8 --

file = open("essay.txt")

print file.encoding

for line in file:
    uline = line.decode("utf-8")
    print type(uline)
    if uline.startswith(u"Fußnote"):
        print "Footnote"
    else:
        print "Other"

и так:

# -- coding: utf-8 --

import codecs
file = codecs.open("essay.txt", encoding="utf-8")

print file.encoding

for line in file:
    print type(line)
    if line.startswith(u"Fußnote"):
        print "Footnote"
    else:
        print "Other"

В первом случае я позволяю Python по умолчанию открывать файл как поток байтов, а затем преобразовывать каждую строку из потока байтов в строку Unicode, используя uline = line.decode("utf-8").

Во втором я открываю файл как файл в кодировке UTF-8, поэтому Python возвращает строки Unicode, когда я перебираю файл.


EDIT

Вот тривиальный способ узнать, содержит ли файл какие-либо данные, отличные от utf8.

import codecs
file = open("baduni.txt")
try:
    for char in codecs.iterdecode(file, "utf-8"):
        print char
except UnicodeDecodeError as e:
    print "error:", e

И пример его использования:

$ echo 'ABC\0200\0101DEF' > baduni.txt
$ od -c baduni.txt
0000000   A   B   C 200   A   D   E   F  \n
0000011
$ python testuni.py
error: 'utf8' codec can't decode byte 0x80 in position 3: invalid start byte

В этом примере 4-й байт (позиция 3, считая от 0) равен 200 восьмеричный / 0x80 шестнадцатеричный.
Статья Wikipedia UTF-8 показывает, что это будет допустимо только как второй байт двухбайтовой последовательности.

0 голосов
/ 27 января 2011

Ваш файл сохраняется в другой кодировке, а не UTF-8. Выясните, в какой кодировке находится файл (возможно CP1252 или около того), и объявите это вместо этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...