pyodbc是如何确定编码的?

3 投票
2 回答
7629 浏览
提问于 2025-04-16 16:58

我已经和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 个回答

4

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。

4

我不能告诉你为什么会有不同,但如果你在你的数据源名称(DSN)中加上“Charset=utf-8”,那么在两台机器上你应该能得到你想要的结果。

免责声明:我在Sybase的SQL Anywhere工程部门工作。

撰写回答