最近我遇到了同样的问题,字段值是字节字符串而不是 unicode。这里有一个小分析。
概述
一般来说,从游标中获取 unicode 值所需要做的就是将charset 参数传递给连接构造函数并具有非二进制表字段(例如utf8_general_ci)。传递use_unicode 是没有用的,因为只要charset 有值,它就会设置为true。
MySQLdb 尊重游标描述字段类型,因此如果游标中有 DATETIME 列,则值将转换为 Python datatime.datetime 实例,DECIMAL 到 decimal.Decimal 等等,但将表示二进制值按原样,按字节字符串。大多数解码器都在MySQLdb.converters 中定义,并且可以通过向连接构造函数提供conv 参数来基于实例覆盖它们。
但 unicode 解码器在这里是个例外,这很可能是设计缺陷。它们是appended directly 到其构造函数中的连接实例转换器。所以只能在实例基础上覆盖它们。
解决方法
让我们看看问题代码。
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()
# (u'abcd\u0451', 'abcd\xd1\x91')
print cursor.description
# (('s', 253, 6, 15, 15, 31, 0), ('b', 251, 6, 50331648, 50331648, 31, 1))
print cursor.description_flags
# (1, 0)
它表明b 字段作为字节字符串而不是 unicode 返回。但是它不是二进制的,MySQLdb.constants.FLAG.BINARY & cursor.description_flags[1] (MySQLdb field flags)。这似乎是库中的错误(打开#90)。但我认为它的原因是 MySQLdb.constants.FIELD_TYPE.LONG_BLOB (cursor.description[1][1] == 251, MySQLdb field types) 根本没有转换器。
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()
# (u'abcd\u0451', u'abcd\u0451')
print cursor.description
# (('s', 253, 6, 15, 15, 31, 0), ('b', 251, 6, 50331648, 50331648, 31, 1))
print cursor.description_flags
# (1, 0)
因此通过操作连接实例converter dict,可以实现所需的unicode解码行为。
如果你想覆盖下面的行为,可能的文本字段的 dict 条目在构造函数之后的样子。
import MySQLdb
import MySQLdb.constants as const
connection = MySQLdb.connect(user = 'guest', db = 'test', charset = 'utf8')
print connection.converter[const.FIELD_TYPE.BLOB]
# [(128, <type 'str'>), (None, <function string_decoder at 0x7fa472dda488>)]
MySQLdb.constants.FLAG.BINARY == 128。这意味着如果一个字段具有二进制标志,它将是str,否则将应用 unicode 解码器。所以你也想尝试转换二进制值,你可以弹出第一个元组。