使用Python将UTF-8字符串写入MySQL

时间:2011-06-01 14:23:18

标签: python unicode utf-8

我正在尝试将用户帐户数据从Active Directory推送到我们的MySQL服务器。这完美无缺,但不知何故,字符串最终会显示变音符号和其他特殊字符的编码版本。

Active Directory使用以下示例格式返回字符串:M\xc3\xbcller

这实际上是Müller的UTF-8编码,但我想将Müller写入我的数据库而不是M\xc3\xbcller

我尝试使用此行转换字符串,但它会在数据库中生成相同的字符串: tempEntry[1] = tempEntry[1].decode("utf-8")

如果我在python控制台中运行print "M\xc3\xbcller".decode("utf-8"),则输出正确。

有没有办法以正确的方式插入此字符串?对于想要拥有这种格式的Web开发人员,我需要这种特定的格式,我不知道他为什么不能直接使用PHP转换字符串。

其他信息:我正在使用MySQLdb;表和列编码为utf8_general_ci

8 个答案:

答案 0 :(得分:50)

正如@ marr75建议的那样,请确保在您的连接上设置charset='utf8'。设置use_unicode=True不是严格必需,因为它是通过设置字符集隐含的。

然后确保将 unicode 对象传递给数据库连接,因为它将使用传递给游标的字符集对其进行编码。如果您传递的是utf8编码的字符串,它将在到达数据库时进行双重编码。

所以,比如:

conn = MySQLdb.connect(host="localhost", user='root', password='', db='', charset='utf8')
data_from_ldap = 'M\xc3\xbcller'
name = data_from_ldap.decode('utf8')
cursor = conn.cursor()
cursor.execute(u"INSERT INTO mytable SET name = %s", (name,))

您也可以通过传递init_command参数来尝试强制连接使用utf8,但我不确定是否需要它。 5分钟的测试应该可以帮助你做出决定。

conn = MySQLdb.connect(charset='utf8', init_command='SET NAMES UTF8')

此外,这几乎不值得一提,因为4.1已经过时了,请确保您使用的是MySQL> = 4.1

答案 1 :(得分:16)

假设您使用的是MySQLdb,则在创建连接时需要传递use_unicode = True和charset =“utf8”。

更新: 如果我针对测试表运行以下内容,我会得到 -

>>> db = MySQLdb.connect(host="localhost", user='root', passwd='passwd', db='sandbox', use_unicode=True, charset="utf8")
>>> c = db.cursor()
>>> c.execute("INSERT INTO last_names VALUES(%s)", (u'M\xfcller', ))
1L
>>> c.execute("SELECT * FROM last_names")
1L
>>> print c.fetchall()
(('M\xc3\xbcller',),)

这是“正确的方法”,正确存储和检索字符,编写php脚本的朋友在输出时没有正确处理编码。

正如Rob指出的那样,use_unicode和charset结合在一起是关于连接的冗长,但我对标准库之外的最有用的python库有一个自然的偏执,所以我试着明确地让bug很容易找到图书馆改变了。

答案 2 :(得分:8)

我找到了解决问题的方法。用.decode('unicode_escape').encode('iso8859-1').decode('utf8')解码字符串最后确实有效。现在一切都按预期插入。完整的其他解决方案可以在这里找到:Working with unicode encoded Strings from Active Directory via python-ldap

答案 3 :(得分:8)

import MySQLdb

# connect to the database
db = MySQLdb.connect("****", "****", "****", "****") #don't use charset here

# setup a cursor object using cursor() method
cursor = db.cursor()

cursor.execute("SET NAMES utf8mb4;") #or utf8 or any other charset you want to handle

cursor.execute("SET CHARACTER SET utf8mb4;") #same as above

cursor.execute("SET character_set_connection=utf8mb4;") #same as above

# run a SQL question
cursor.execute("****")

#and make sure the MySQL settings are correct, data too

答案 4 :(得分:5)

最近我遇到了同样的问题,字段值是字节字符串而不是unicode。这里有一点分析。

概述

通常,所有人都需要从游标中获取unicode值,是将charset参数传递给连接构造函数,并且具有非二进制表字段(例如utf8_general_ci)。传递use_unicode是没用的,因为只要charset有值,它就会设置为true。

MySQLdb尊重游标描述字段类型,因此如果游标中有DATETIME列,则值将转换为Python datatime.datetime实例,DECIMAL转换为decimal.Decimal等等on,但二进制值将按字节字符串表示。大多数解码器都在MySQLdb.converters中定义,并且可以通过向连接构造函数提供conv参数来基于实例覆盖它们。

但unicode解码器在这里是一个例外,这可能是设计上的缺点。它们是appended directly在其构造函数中连接实例转换器。所以它只能在instance-basic上覆盖它们。

解决方法

让我们看看问题代码。

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)。它似乎是库中的bug(打开#90)。但我认为MySQLdb.constants.FIELD_TYPE.LONG_BLOBcursor.description[1][1] == 251MySQLdb 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解码器。所以你想尝试转换二进制值,你可以弹出第一个元组。

答案 5 :(得分:2)

(想回复上面的回答,但没有足够的声誉......)

在这种情况下,您没有获得unicode的原因是:

>>> print c.fetchall()
(('M\xc3\xbcller',),)

是来自MySQLdb 1.2.x的错误,带有* _bin整理,请参阅:

http://sourceforge.net/tracker/index.php?func=detail&aid=1693363&group_id=22307&atid=374932 http://sourceforge.net/tracker/index.php?func=detail&aid=2663436&group_id=22307&atid=374932

在这种特殊情况下(整理 utf8_bin - 或[任何] _bin ...)你必须要有“原始”值,这里是utf-8(是的,这很糟糕,因为没有通用修复)。

答案 6 :(得分:0)

和db.set_character_set('utf8'),意味着 use_unicode = True?

答案 7 :(得分:0)

还有一种情况可能有点罕见。

如果你首先在mysqlworkbench中创建一个模式,你将得到编码错误,并且无法通过添加charset配置来解决它。

这是因为默认情况下mysqlworkbench由latin1创建模式,所以你应该首先设置charset! enter image description here