Путаница в Python UTF8 - PullRequest
       3

Путаница в Python UTF8

1 голос
/ 11 июля 2010

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

x = '\xd0\xa4'
y = '\x92'

В оболочке Python я получаю следующее:

print x
Ф
print y
?

Именно это я и хочу увидеть,Однако тогда есть следующее:

print unicode(x, 'utf8')
Ф

Но не это:

unicode(y, 'utf8')
UnicodeDecodeError: 'utf8' codec can't decode byte 0x92 in position 0: unexpected code byte

Мне кажется, что наши строки искажаются, потому что Django пытается преобразовать их в Unicode, но я 'Я просто догадываюсь на данный момент.Любые идеи или обходные пути приветствуются.

ОБНОВЛЕНИЕ : Когда я смотрю на базу данных в строке, содержащей значение '\ x92', я вижу этот символ как '.АпострофЯ просматриваю содержимое базы данных, используя кодировку Unicode UTF-8.

Ответы [ 5 ]

7 голосов
/ 11 июля 2010

Похоже, у вас есть опечатка; должно быть x = '\xd0\xa4'. Это очень помогает, если вы используете копирование и вставку того, что вы действительно запустили, и того, что появилось на выходе.

"\ x92" не является допустимой строкой UTF-8. Это объясняет исключение, которое вы получили.

Еще одна загадка - вот почему print y произвел ?. Что вы называете "консолью Python" ?? Кажется, он работает в режиме «заменить» и заменяет «?» ... вы уверены, что это просто "?" а не белый "?" внутри черного алмаза? Почему ты это сказал "?" это именно то, что вы ожидаете увидеть?

ОБНОВЛЕНИЕ: Теперь вы говорите «» «Когда я смотрю в базу данных на строку, содержащую значение« \ x92 », я вижу этот символ как«. Апостроф. содержимое базы данных с использованием кодировки Unicode UTF-8. "" "

Это не апостроф. Кажется, что эта часть данных была закодирована с использованием одной из кодировок cp125X (она же windows-125X). Иллюстрирование с использованием cp1252 (обычный подозреваемый):

IDLE 2.6.4      
>>> import unicodedata
>>> uc = '\x92'.decode('cp1252')
>>> print repr(uc)
u'\u2019'
>>> print uc
’
>>> unicodedata.name(uc)
'RIGHT SINGLE QUOTATION MARK'
>>> 

Вместо «просмотра содержимого базы данных с использованием кодировки Unicode UTF-8» (что бы это ни значило), попробуйте написать небольшой фрагмент кода Python для извлечения ошибочной строки и затем выполните print repr(bad_string). Покажите нам код, который вы запустили, плюс вывод repr (). Также скажите нам, какая версия Python, какая платформа (на базе Windows или Unix) и какая версия программного обеспечения для баз данных. И часть оператора CREATE TABLE, относящаяся к рассматриваемому столбцу.

Также, пожалуйста, прочитайте это и это .

5 голосов
/ 11 июля 2010

\x92 не является допустимым символом в кодировке utf-8.

Вы не замечаете этого, потому что вы используете простые (не-юникодные) строки для x и y, пока не попытаетесь декодировать их в строки Юникода. Когда вы затем распечатываете их, они просто выгружаются в терминал «как есть», и сам терминал интерпретирует байты в соответствии с настройкой кодирования.

В unicode() есть третий параметр, который сообщает Python, что делать в случае ошибок кодирования (декодирования):

>>> unicode('\x92', 'utf8', 'replace')
u'\ufffd'
>>> print _
�
4 голосов
/ 11 июля 2010

Я думал, что любой символ Unicode, кроме подмножества ASCII, имел многобайтовое представление в UTF-8.Ваш y имеет смысл как строка с одним байтом на символ, но не как строка UTF-8.Поскольку один байт находится вне диапазона ASCII от 0x00 до 0x7F, кодек будет ожидать дополнительный байт или более для преобразования в «настоящий» символ Юникода.

Я не так знаком с Python, как когда-тоБыл, однако, и я не уверен в этом ответе.

РЕДАКТИРОВАТЬ прыжки является лучшим ответом ИМО.

2 голосов
/ 11 июля 2010

Теперь я вижу, где вы запутались. Давайте посмотрим на это:

x = '\xd0\xa4'
y = '\x92'

Если я print x, я получаю Ф. Это потому, что мой терминал использует UTF-8 в качестве кодировки символов. Таким образом, когда он получает D0 A4, он пытается декодировать его как UTF-8 и получает «Ф». Если я изменю свой терминал на использование, скажем, ISO-8859-1 ("latin1"), и я скажу print x, мой терминал попытается декодировать D0 A4 с использованием ISO-8859-1, и, поскольку D0 A4 также допустимая строка ISO-8859-1, она будет декодироваться, но на этот раз в «Ð¤».

Теперь для print y. Это не строка UTF-8, поэтому мой терминал не может это декодировать. Это показывает мне эту ошибку, в моем случае, печатая « ». Мне интересно, если вы видите " " или "?" - вы, вероятно, должны увидеть первое, но это зависит от того, что ваш терминал делает перед лицом плохого вывода.

Кодировка вашего терминала должна совпадать с тем, что говорит $LANG, и ваша программа должна выводить данные в любой кодировке, указанной $LANG. В настоящее время $LANG обычно составляет ???.UTF-8, где ??? изменяется. (Мой en_US.UTF-8)

Теперь, когда вы говорите unicode(y, 'utf8'), Python пытается декодировать это как UTF-8 и соответственно выдает исключение.

Я использую Gnome Terminal и могу изменить свою кодировку, перейдя в Терминал → Установить кодировку символов

1 голос
/ 11 июля 2010
0x92 (hex) = 10 010010 (binary)

Поскольку UTF-8 может представлять 010010 в одном байте, «заголовок» должен быть 0 (-> 00010010) вместо 10 (который никогда не может быть заголовком первого байта).Символы не могут быть представлены с большим количеством байтов, чем необходимо, поэтому «\ x92» не является допустимой строкой в ​​кодировке UTF-8.

Я полагаю, что ваша база данных использует некоторую кодировку по одному байту на символ (например, латинский)-1).Если вы сами кодируете запросы к базе данных, вы должны убедиться, что кодировка соединения правильная или строки правильно декодированы.В моделях Django все должно работать автоматически.

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