最近我遇到了一个问题,即字段值是字节字符串而不是Unicode。以下是一些分析。
概述
通常来说,要从游标得到Unicode值,唯一需要做的就是在连接构造函数中传递charset
参数,并且拥有非二进制表字段(例如utf8_general_ci
)。传递use_unicode
是无用的,因为只要charset
有值,它就被设置为true。
MySQLdb尊重游标描述字段类型,因此如果游标中有一个DATETIME
列,则这些值将被转换为Python datatime.datetime
实例,DECIMAL
转换为decimal.Decimal
等等,但二进制值将用字节字符串表示。大多数解码器都在MySQLdb.converters
中定义,可以通过为连接构造函数提供conv
参数来按实例覆盖它们。
但是Unicode解码器在这里是个例外,这很可能是设计上的缺陷。它们直接附加到连接实例转换器中的构造函数。因此,只能在实例上覆盖它们。
解决方法
让我们来看看出现问题的代码。
import MySQLdb
connection = MySQLdb.connect(user = 'guest', db = 'test', charset = 'utf8')
cursor = connection.cursor()
cursor.execute(u"SELECT 'abcdё' `s`, ExtractValue('<a>abcdё</a>', '/a') `b`")
print cursor.fetchone()
print cursor.description
print cursor.description_flags
它显示
b
字段返回为字节字符串而不是Unicode。但它不是二进制的,
MySQLdb.constants.FLAG.BINARY & cursor.description_flags[1]
(
MySQLdb字段标志)。这似乎是库中的错误(已经开放了
#90)。但我认为原因是
MySQLdb.constants.FIELD_TYPE.LONG_BLOB
(
cursor.description[1][1] == 251
,
MySQLdb字段类型)根本没有转换器。
import MySQLdb
import MySQLdb.converters as conv
import MySQLdb.constants as const
connection = MySQLdb.connect(user = 'guest', db = 'test', charset = 'utf8')
connection.converter[const.FIELD_TYPE.LONG_BLOB] = connection.converter[const.FIELD_TYPE.BLOB]
cursor = connection.cursor()
cursor.execute(u"SELECT 'abcdё' `s`, ExtractValue('<a>abcdё</a>', '/a') `b`")
print cursor.fetchone()
print cursor.description
print cursor.description_flags
因此,通过操作连接实例
converter
字典,可以实现所需的Unicode解码行为。
如果您想要覆盖默认行为,以下是构造函数后可能文本字段的字典条目。
import MySQLdb
import MySQLdb.constants as const
connection = MySQLdb.connect(user = 'guest', db = 'test', charset = 'utf8')
print connection.converter[const.FIELD_TYPE.BLOB]
MySQLdb.constants.FLAG.BINARY == 128
。这意味着,如果一个字段有二进制标志,它将是str
,否则将应用unicode解码器。因此,如果您想尝试转换二进制值,可以弹出第一个元组。