我正在尝试将用户帐户数据从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
答案 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_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解码器。所以你想尝试转换二进制值,你可以弹出第一个元组。
答案 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)