pyodbc是如何确定编码的?
我已经和Sybase SQL Anywhere 12以及Python(还有Twisted)斗争了好几个星期,终于把我的东西搞定了。
现在只剩下一个小烦恼:当我在CentOS 5上运行我的脚本,使用的是定制的Python 2.7.1(这是我部署的平台)时,得到的结果是UTF-8格式。
但如果我在我的Ubuntu机器(Natty Narwhal)上运行,就会得到latin1格式的结果。
当然,我更希望能得到Unicode格式的数据,不过这不是我这次提问的重点。:)
这两台机器都是64位的,都是用相同的方式安装的Python 2.7.1,使用的是UCS4,还有自编译的unixODBC 2.3.0。
我对此感到很困惑,找不到相关的文档。为什么pyodbc或unixODBC在这两台机器上的表现会不一样呢?
一些硬性事实:
- Python: 2.7.1
- 数据库: SQL Anywhere 12
- unixODBC: 2.3.0(2.2.14的表现也是一样),使用相同的编译选项自编译的
- ODBC驱动: 来自Sybase的原版。
- CentOS 5给我的是UTF-8,而Ubuntu Natty Narwhal给我的是latin1。
我的odbc.ini文件是这样的:
[sybase]
Uid = user
Pwd = password
Driver = /opt/sqlanywhere/lib64/libdbodbc12_r.so
Threading = True
ServerName = dbname
CommLinks = tcpip(host=the-host;DoBroadcast=None)
我只是通过DNS='sybase'来连接。
谢谢大家!
2 个回答
pyodbc是一个用来连接数据库的工具,它遵循ODBC这个标准。ODBC标准只支持两种编码方式。所有以'W'结尾的ODBC函数都是宽字符版本,使用的是SQLWCHAR。这种编码通常是UCS2,有时也会是UCS4。而不带'W'的版本使用的是SQLCHAR,通常是单字节的ANSI或ASCII编码。
ODBC根本不支持像UTF8这样的可变宽度编码。如果ODBC驱动程序提供了这种支持,那就是错误的。即使数据是用UTF8存储的,驱动程序也必须把它转换成ANSI或UCS2。不幸的是,大多数ODBC驱动程序都完全不正确。
在发送数据给驱动程序时,如果数据是'str'对象,pyodbc会使用ANSI编码;如果数据是'unicode'对象,它会使用UCS2或UCS4(具体取决于你平台上SQLWCHAR的定义)。驱动程序在返回数据时会决定是SQLCHAR还是SQLWCHAR,而pyodbc对此没有任何控制。如果是SQLCHAR,它会被转换成'str'对象;如果是SQLWCHAR,则会转换成'unicode'对象。
对于3.x版本来说,情况会稍有不同,默认情况下会把SQLCHAR和SQLWCHAR都转换成Unicode。
我不能告诉你为什么会有不同,但如果你在你的数据源名称(DSN)中加上“Charset=utf-8”,那么在两台机器上你应该能得到你想要的结果。
免责声明:我在Sybase的SQL Anywhere工程部门工作。