从pyodbc调用MS SQL Server的编码问题
我正在通过 SQL Alchemy 连接到 MS SQL 服务器,使用的是 pyodbc 模块。一切看起来都很正常,直到我开始遇到编码的问题。有些非 ASCII 字符被替换成了 '?'。
数据库的排序规则是 'Latin1_General_CI_AS'(我也检查了具体的字段,它们保持相同的排序规则)。我开始在 create_engine
的调用中选择 'latin1' 编码,这对西欧字符(比如法语或西班牙语的字符,如 é
)似乎有效,但对东欧字符就不行了。具体来说,我遇到了字符 ć
的问题。
我尝试选择其他编码,正如 Python 文档所述,特别是微软的编码,比如 cp1250
和 cp1252
,但我仍然面临同样的问题。
有没有人知道如何解决这些差异?'Latin1_General_CI_AS' 的排序规则在 Python 编码中有对应吗?
我当前连接的代码如下:
for sqlalchemy import *
def connect():
return pyodbc.connect('DSN=database;UID=uid;PWD=password')
engine = create_engine('mssql://', creator=connect, encoding='latin1')
connection = engine.connect()
说明和评论:
- 这个问题发生在从数据库检索信息时。我不需要存储任何东西。
- 一开始我没有指定编码,结果是每当数据库中遇到非 ASCII 字符时,pyodbc 就会抛出 UnicodeDecodeError。我通过使用 'latin1' 作为编码来纠正这个问题,但这并没有解决所有字符的问题。
- 我承认服务器并不是使用 latin1,之前的评论不正确。我一直在检查数据库的排序规则和具体字段的排序规则,似乎都是 'Latin1_General_CI_AS',那么
ć
是怎么存储的呢?也许我对排序规则的理解不够准确。 - 我稍微修改了一下问题,具体来说,我尝试了比
latin1
更多的编码,包括cp1250
和cp1252
(根据 msdn,这似乎是 'Latin1_General_CI_AS' 使用的编码)。
更新:
好的,按照这些步骤,我发现数据库使用的编码似乎是 cp1252:http://bytes.com/topic/sql-server/answers/142972-characters-encoding。不过,这似乎是一个错误的假设,答案中也反映了这一点。
更新2:
无论如何,在正确配置 ODBC 驱动程序后,我在 Python 代码中不需要指定编码。
4 个回答
试着用 pyodbc.connect() 这个方法连接数据库,并设置参数 convert_unicode=True
,比如在使用 sqlalchemy 的时候:
engine = create_engine('mssql://yourdb', connect_args={'convert_unicode': True})
这样做可以确保你得到的所有结果(不仅仅是 nvarchar
等类型的结果)都是 Unicode 格式的,这些结果会根据数据库使用的编码正确转换过来。
至于往数据库写数据,记得始终使用 Unicode。如果我没记错的话(稍后会确认),pyodbc 会确保这些数据也能正确写入数据库。
当然,如果数据库使用的编码不支持你想写入的字符,你还是会遇到错误:如果你希望列能够支持任何字符,你也需要在数据库中使用 Unicode 列。
原评论变成的回答:
cp1250和cp1252并不是“latin1编码”。排序规则和编码是两回事。关于你的评论:谁说“服务器是用latin1编码的”?如果服务器要求所有的输入/输出都用latin1编码(我对此表示怀疑),那么你就无法把一些东欧字符存入数据库(包括俄语、中文、希腊语等等)。
更新:
你需要关注的不仅仅是排序规则。"""msdn.microsoft.com/en-us/library/ms174596(v=SQL.90).aspx提到,对于Latin1_General_CI_AS使用的编码是cp1252"""这句话是错误的。这个表提供了每个地区的LCID(地区标识符)、默认排序规则和代码页。没错,排序规则“Latin1_General_CI_AS”确实和cp1252代码页在几个地区是相关的。但是在两个地区(亚美尼亚和格鲁吉亚),它却和“Unicode”代码页有关联(!!!)。
简单来说,你需要找出数据库使用的代码页。
尝试从数据库中提取数据时,不要指定任何编码。不要去猜测你的控制台可能使用的编码,这样只会增加混淆。相反,使用print repr(data)
。然后把你在预期非Latin1字符的地方从repr()得到的结果反馈回来。
你应该停止使用代码页,改用Unicode。这是解决这类问题的唯一方法。