Latin-1 и фабрика юникодов в Python - PullRequest
5 голосов
/ 21 июля 2009

У меня есть сценарий Python 2.6, который заглатывает специальные символы, закодированные в Latin-1, которые я извлекаю из базы данных SQL Server. Я хотел бы напечатать эти символы, но я несколько ограничен, потому что я использую библиотеку, которая вызывает фабрику unicode, и я не знаю, как заставить Python использовать кодек, отличный от ascii.

Сценарий - это простой инструмент для возврата данных поиска из базы данных без необходимости выполнения SQL непосредственно в редакторе SQL. Я использую библиотеку PrettyTable 0.5 для отображения результатов.

Суть скрипта - это бит кода. Кортежи, которые я получаю от курсора, содержат целочисленные и строковые данные, а не данные Unicode. (Я бы использовал adodbapi вместо pyodbc, что дало бы мне Unicode, но adodbapi доставляет мне другие проблемы.)

x = pyodbc.connect(cxnstring)
r = x.cursor()
r.execute(sql)

t = PrettyTable(columns)
for rec in r:
    t.add_row(rec)
r.close()
x.close()

t.set_field_align("ID", 'r')
t.set_field_align("Name", 'l')
print t

Но столбец Name может содержать символы, выходящие за пределы диапазона ASCII. Иногда я получаю сообщение об ошибке, подобное этому, в строке 222 из prettytable.pyc, когда оно достигает вызова t.add_row:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xed in position 12: ordinal not in range(128)

Это строка 222 в prettytable.py. Он использует unicode, который является источником моих проблем, и не только в этом скрипте, но и в других скриптах Python, которые я написал.

for i in range(0,len(row)):
    if len(unicode(row[i])) > self.widths[i]:   # This is line 222
        self.widths[i] = len(unicode(row[i]))

Пожалуйста, скажите мне, что я здесь делаю неправильно. Как я могу заставить unicode работать без взлома prettytable.py или любой другой библиотеки, которую я использую? Есть ли способ сделать это?

EDIT: ошибка возникает не в операторе print, а в вызове t.add_row.

РЕДАКТИРОВАТЬ: С помощью Бастиена Леонара, я пришел к следующему решению. Это не панацея, но она работает.

x = pyodbc.connect(cxnstring)
r = x.cursor()
r.execute(sql)

t = PrettyTable(columns)
for rec in r:
    urec = [s.decode('latin-1') if isinstance(s, str) else s for s in rec]
    t.add_row(urec)
r.close()
x.close()

t.set_field_align("ID", 'r')
t.set_field_align("Name", 'l')
print t.get_string().encode('latin-1')

Мне пришлось декодировать при входе и кодировать при выходе. Все это заставляет меня надеяться, что все перенесут свои библиотеки в Python 3.x раньше, чем позже!

Ответы [ 3 ]

5 голосов
/ 21 июля 2009

Добавьте это в начале модуля:

# coding: latin1

Или расшифруйте строку в Unicode самостоятельно.

[Изменить]

Прошло много времени с тех пор, как я играл с Unicode, но, надеюсь, этот пример покажет, как конвертировать из Latin1 в Unicode:

>>> s = u'ééé'.encode('latin1') # a string you may get from the database
>>> s.decode('latin1')
u'\xe9\xe9\xe9'

[редактировать]

Документация:
http://docs.python.org/howto/unicode.html
http://docs.python.org/library/codecs.html

2 голосов
/ 21 июля 2009

Может быть, попытаться декодировать строки в кодировке latin1 в Unicode?

t.add_row((value.decode('latin1') for value in rec))
0 голосов
/ 21 июля 2009

После быстрого просмотра источника для PrettyTable выясняется, что он работает с объектами Юникода внутри (см., Например, _stringify_row, add_row и add_column). Поскольку он не знает, какую кодировку используют ваши входные строки, он использует кодировку по умолчанию: обычно ascii .

Теперь ascii - это подмножество латиницы-1, что означает, что если вы конвертируете из ascii в латиницу-1, у вас не должно возникнуть проблем. Обратное, однако, не соответствует действительности; не все символы латиницы 1 отображаются на символы ascii. Чтобы продемонстрировать это:

>>> s = u'\xed\x31\x32\x33'
>>> print s
# FAILS: Python calls "s.decode('ascii')", but ascii codec can't decode '\xed'
>>> print s.decode('ascii')
# FAILS: Same as above
>>> print s.decode('latin-1')
í123

Явное преобразование строк в Unicode (как вы в конечном итоге сделали) исправляет вещи и имеет больше смысла, IMO - вы с большей вероятностью узнаете, какую кодировку используют ваши данные, чем автор PrettyTable :). Кстати, вы можете пропустить проверку строк в вашем понимании списка, заменив s.decode('latin-1') на unicode(s, 'latin-1'), поскольку все объекты можно привести к строкам .

И последнее: не забудьте проверить набор символов вашей базы данных и таблиц - вы не хотите использовать в коде «latin-1», когда данные на самом деле хранятся как-то еще (' utf-8 '?) в базе данных. В MySQL вы можете использовать команду SHOW CREATE TABLE <table_name>, чтобы узнать, какой набор символов использует таблица, и SHOW CREATE DATABASE <db_name>, чтобы сделать то же самое для базы данных.

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