Python中的Latin-1和unicode工厂

Python中的Latin-1和unicode工厂,python,unicode,Python,Unicode,我有一个Python2.6脚本,它使用了特殊字符,用拉丁语1编码,我正在从SQLServer数据库检索这些字符。我想打印这些字符,但我有点受限,因为我使用的是一个调用unicode工厂的库,我不知道如何让Python使用ascii以外的编解码器 该脚本是一个从数据库返回查找数据的简单工具,无需在SQL编辑器中直接执行SQL。我使用0.5库来显示结果 脚本的核心是这段代码。我从游标获得的元组包含整数和字符串数据,没有Unicode数据。(我会使用adodbapi而不是pyodbc,这将使我获得Un

我有一个Python2.6脚本,它使用了特殊字符,用拉丁语1编码,我正在从SQLServer数据库检索这些字符。我想打印这些字符,但我有点受限,因为我使用的是一个调用
unicode
工厂的库,我不知道如何让Python使用
ascii
以外的编解码器

该脚本是一个从数据库返回查找数据的简单工具,无需在SQL编辑器中直接执行SQL。我使用0.5库来显示结果

脚本的核心是这段代码。我从游标获得的元组包含整数和字符串数据,没有Unicode数据。(我会使用
adodbapi
而不是
pyodbc
,这将使我获得Unicode,但是
adodbapi
给我带来了其他问题。)

但是
Name
列可以包含超出ASCII范围的字符。我有时会在
prettytable.pyc
的第222行收到这样的错误消息,当它到达
t.add\u行时
调用:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xed in position 12: ordinal not in range(128)
这是
prettytable.py
中的第222行。它使用了
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
或我使用的任何其他库?有没有办法做到这一点

编辑:错误不是发生在
print
语句中,而是发生在
t.add\u行
调用中

编辑:在巴斯蒂安·莱昂纳德的帮助下,我想出了以下解决方案。这不是灵丹妙药,但很管用

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')

我最后不得不在进来的时候解码,在出去的时候编码。所有这些都让我希望每个人都能尽早将他们的库移植到Python3.x上

在模块开头添加以下内容:

# 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'
[编辑]

文档:


是否可以尝试将拉丁文编码的字符串解码为unicode

t.add_row((value.decode('latin1') for value in rec))

快速浏览PrettyTable的源代码后,它似乎在内部对unicode对象起作用(例如,请参见
\u stringify\u row
add\u row
add\u column
)。因为它不知道输入字符串使用的是什么编码,所以它使用默认编码

现在ascii是latin-1的一个子集,这意味着如果要从ascii转换为latin-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(就像您最终所做的那样)修复了一些问题,而且更有意义,在我看来——与PrettyTable:)的作者相比,您更可能知道数据使用的是什么字符集。顺便说一句,您可以通过将
s.decode('latin-1')
替换为
unicode(s,'latin-1')
来省略列表理解中的字符串检查,因为所有对象都可以强制为字符串


最后一件事:别忘了检查数据库和表的字符集——当数据实际上作为其他内容(“utf-8”)存储在数据库中时,您不希望在代码中使用“拉丁-1”。在MySQL中,您可以使用
SHOW CREATE TABLE
命令来查找表使用的字符集,并使用
SHOW CREATE DATABASE
对数据库执行相同的操作。

我已经尝试将编码放在脚本的顶部,但仍然不起作用。我将尝试显式解码,但我希望有一个更通用的解决方案。您可能不想设置编码:latin1。这会改变脚本源代码的编码,而不是它的数据。@Glenn:我建议这样做是因为我认为
print t t
可以打印拉丁文原始字符串。我的脚本没有任何带有非ASCII字符的字符串文字。所以这不应该是一个因素。@Bastien Léonard:我从数据库中获得的数据不是Unicode。所讨论的字符是0xed。我无法用ascii编解码器解码0xed。有没有办法只为这一个函数更改默认编码,这样
unicode
就可以工作?t.add_row([s.decode('latin-1'),如果是instance(s,str),那么s代表rec中的s。)#我想你是这个意思(或者类似的意思)。可能,这取决于prettytable需要什么。如果它在内部使用unicode对象,应该有一种方法可以取回Unicode对象并避免无意义地来回转换它们。只要您始终使用Unicode对象,就可以避免大部分这种混乱(Python3就是这样工作的)。我相信它确实做到了:“get_string”。@elo80ka,数据实际上存储为拉丁语-1。我在写剧本之前验证了这一点。另外(至少在Python2.6中),不能使用
unicode(int\u值,'latin-1')
强制int,即使
unicode(int\u值)
有效@Glenn Maynard,打印结果涉及解码,无论是否明确定义。我不得不使用
t.get_string().encode('latin-1')
是的,我期待着py3k的广泛采用,以便所有字符串都是Unicode。这会省去很多麻烦。@eksortso:你说得对……字符集对数字来说根本没有意义。
>>> 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