Проблема декодирования строки Python - PullRequest
3 голосов
/ 05 марта 2010

Я пытаюсь проанализировать CSV-файл, содержащий некоторые данные, в основном числовые, но с некоторыми строками - я не знаю их кодировку, но знаю, что они на иврите.

В конце концов мне нужно знатькодировку, чтобы я мог кодировать строки в юникоде, распечатывать их и, возможно, позже выбрасывать их в базу данных.

Я пытался использовать Chardet , которая утверждает, что строки - Windows-1255 (cp1255) но попытка сделать print someString.decode('cp1255') приводит к печально известной ошибке:

UnicodeEncodeError: 'ascii' codec can't encode characters in position 1-4: ordinal not in range(128)

Я пробовал все возможные варианты кодирования, но безрезультатно.Кроме того, файл абсолютно действителен, так как я могу открыть CSV в Excel и вижу правильные данные.

Есть идеи, как правильно декодировать эти строки?


РЕДАКТИРОВАТЬ: вот пример.Одна из строк выглядит следующим образом (первые пять букв еврейского алфавита):

print repr(sampleString)
#prints:
'\xe0\xe1\xe2\xe3\xe4'

(с использованием Python 2.6.2)

Ответы [ 4 ]

12 голосов
/ 05 марта 2010

Вот что происходит:

  • sampleString является байтовой строкой (в кодировке cp1255)
  • sampleString.decode("cp1255") декодирует (decode == bytes -> unicode string) строку байта в строку Unicode
  • print sampleString.decode("cp1255") пытается вывести строку юникода на стандартный вывод. Для этого при печати необходимо кодировать строку в кодировке Unicode (кодировать == строка в кодировке Unicode -> байты). Ошибка, которую вы видите, означает, что оператор печати python не может записать данную строку юникода в кодировку консоли. sys.stdout.encoding - кодировка терминала.

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

Другой подход заключается в ручном указании используемой кодировки:

print sampleString.decode("cp1255").encode("utf-8")

Смотри также:

Простая тестовая программа, с которой вы можете поэкспериментировать:

import sys
print sys.stdout.encoding
samplestring = '\xe0\xe1\xe2\xe3\xe4'
print samplestring.decode("cp1255").encode(sys.argv[1])

На моем терминале utf-8:

$ python2.6 test.py utf-8
UTF-8
אבגדה

$ python2.6 test.py latin1
UTF-8
Traceback (most recent call last):
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 0-4: ordinal not in range(256)

$ python2.6 test.py ascii
UTF-8
Traceback (most recent call last):
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)

$ python2.6 test.py cp424
UTF-8
ABCDE

$ python2.6 test.py iso8859_8
UTF-8
�����

Сообщения об ошибках для latin-1 и ascii означают, что символы Unicode в строке не могут быть представлены в этих кодировках.

Обратите внимание на последние два. Я кодирую строку Unicode в кодировки cp424 и iso8859_8 (две из кодировок, перечисленных в http://docs.python.org/library/codecs.html#standard-encodings, которые поддерживают ивритские символы). Я не получаю исключений при использовании этих кодировок, так как еврейские символы юникода имеют представление в кодировках.

Но мой терминал utf-8 сильно запутывается, когда получает байты в кодировке, отличной от utf-8.

В первом случае (cp424), мой терминал UTF-8 отображает ABCDE, что означает, что представление A utf-8 соответствует представлению cp424 ה, то есть значение 65 байта означает A в utf-8 и ה в cp424.

Метод encode имеет необязательный строковый аргумент, который можно использовать, чтобы указать, что должно происходить, когда кодировка не может представлять символ ( документация ). Поддерживаемые стратегии являются строгими (по умолчанию), ignore, replace, xmlcharref и backslashreplace. Вы даже можете добавить свои собственные стратегии .

Другая тестовая программа (я печатаю с кавычками вокруг строки, чтобы лучше показать, как игнорирует себя):

import sys
samplestring = '\xe0\xe1\xe2\xe3\xe4'
print "'{0}'".format(samplestring.decode("cp1255").encode(sys.argv[1], 
      sys.argv[2]))

Результаты:

$ python2.6 test.py latin1 strict
Traceback (most recent call last):
  File "test.py", line 4, in <module>
    sys.argv[2]))
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 0-4: ordinal not in range(256)
[/tmp]
$ python2.6 test.py latin1 ignore
''
[/tmp]
$ python2.6 test.py latin1 replace
'?????'
[/tmp]
$ python2.6 test.py latin1 xmlcharrefreplace
'&#1488;&#1489;&#1490;&#1491;&#1492;'
[/tmp]
$ python2.6 test.py latin1 backslashreplace
'\u05d0\u05d1\u05d2\u05d3\u05d4'
3 голосов
/ 05 марта 2010

Когда вы декодируете строку в Unicode с помощью someString.decode('cp1255'), вы получаете абстрактное представление некоторого текста на иврите в Unicode. (Эта часть успешно выполняется!) Когда вы используете print, вам нужно конкретное закодированное представление в определенной кодировке. Похоже, ваша проблема не в декодировании, а в print.

Для печати либо просто print someString, если ваш терминал понимает cp1255, либо "print someString.decode('cp1255').encode('the_encoding_your_terminal_does_understand')". Если вам не нужно, чтобы полученный отпечаток читался как на иврите, print repr(someString.decode('cp1255')) также дает вам осмысленное представление абстрактной строки юникода.

0 голосов
/ 05 марта 2010

Вы получаете ошибку кодирования при печати, так что, скорее всего, она хорошо декодируется, вы просто не сможете распечатать результат должным образом. Попробуйте запустить chcp 65001 в командной строке перед запуском кода Python.

0 голосов
/ 05 марта 2010

Является ли someString, возможно, не обычной строкой, а строкой в ​​юникоде, как вы бы хотели, чтобы мы поверили с вашим sampleString?

>>> print '\xe0\xe1\xe2\xe3\xe4'.decode('cp1255')
<hebrew characters>

>>> print u'\xe0\xe1\xe2\xe3\xe4'.decode('cp1255')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "[...]/encodings/cp1255.py", line 15, in decode
    return codecs.charmap_decode(input,errors,decoding_table)
UnicodeEncodeError: 'ascii' codec can't encode characters [...]
...