使用 Python 将 UTF-8 字符串写入 MySQL
- 2025-04-10 09:47:00
- admin 原创
- 18
问题描述:
我正在尝试将用户帐户数据从 Active Directory 推送到我们的 MySQL 服务器。此过程完美无缺,但不知何故,字符串最终显示了变音符号和其他特殊字符的编码版本。
Active Directory 使用以下示例格式返回字符串:Mxc3xbcller
这实际上是的 UTF-8 编码Müller
,但我想将其写入Müller
我的数据库而不是Mxc3xbcller
。
我尝试使用此行转换字符串,但它在数据库中产生相同的字符串:tempEntry[1] = tempEntry[1].decode("utf-8")
如果我print "Mxc3xbcller".decode("utf-8")
在 python 控制台中运行,输出是正确的。
有什么方法可以正确插入此字符串?对于想要这种精确格式的 Web 开发人员,我需要这种特定格式,我不知道为什么他不能直接使用 PHP 转换字符串。
附加信息:我正在使用 MySQLdb;表和列编码是 utf8_general_ci
解决方案 1:
正如@marr75 所建议的,请确保charset='utf8'
在连接上进行设置。设置use_unicode=True
并不是绝对必要的,因为它是通过设置字符集来暗示的。
然后确保将unicode对象传递给数据库连接,因为它将使用传递给游标的字符集对其进行编码。如果您传递的是 utf8 编码的字符串,则它在到达数据库时将被双重编码。
因此,类似于:
conn = MySQLdb.connect(host="localhost", user='root', password='', db='', charset='utf8')
data_from_ldap = 'Mxc3xbcller'
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
解决方案 2:
假设您正在使用 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'Mxfcller', ))
1L
>>> c.execute("SELECT * FROM last_names")
1L
>>> print c.fetchall()
(('Mxc3xbcller',),)
这是“正确的方式”,字符被正确地存储和检索,你编写 php 脚本的朋友只是在输出时没有正确处理编码。
正如 Rob 指出的那样,use_unicode 和 charset 结合起来对于连接来说过于冗长,但是我对标准库之外最有用的 Python 库也有一种天生的偏执,因此我尝试明确说明,以便在库发生变化时能够轻松找到错误。
解决方案 3:
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:
我找到了解决问题的方法。用 解码字符串.decode('unicode_escape').encode('iso8859-1').decode('utf8')
终于成功了。现在一切都按预期插入了。完整的其他解决方案可以在这里找到:通过 python-ldap 使用 Active Directory 中的 unicode 编码字符串
解决方案 5:
最近我也遇到了同样的问题,字段值是字节字符串而不是unicode。下面是一些分析。
概述
通常,要从游标获取 unicode 值,只需将charset
参数传递给连接构造函数并具有非二进制表字段(例如utf8_general_ci
)。传递是无用的,因为只要有值,use_unicode
它就会设置为 true 。charset
MySQLdb 尊重游标描述字段类型,因此如果游标中有DATETIME
列,则值将转换为 Pythondatatime.datetime
实例,DECIMAL
等等decimal.Decimal
,但二进制值将按原样表示为字节字符串。大多数解码器都在中定义MySQLdb.converters
,可以通过向连接构造函数提供参数来在实例基础上覆盖它们conv
。
但 unicode 解码器在这里是个例外,这可能是一个设计缺陷。它们直接附加到其构造函数中的连接实例转换器。因此只能在 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', 'abcdxd1x91')
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 字段标志)。这似乎是库中的错误(已打开#90)。但我认为其原因是MySQLdb.constants.FIELD_TYPE.LONG_BLOB
(MySQLdb 字段类型cursor.description[1][1] == 251
)根本没有转换器。
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
字典,可以实现所需的unicode解码行为。
如果您想覆盖该行为,这里是构造函数之后可能的文本字段的字典条目的样子。
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 解码器。因此,如果您也想尝试转换二进制值,则可以弹出第一个元组。
解决方案 6:
(想回复以上答案但声誉不够......)
在这种情况下无法获得unicode结果的原因是:
>>> print c.fetchall()
(('Mxc3xbcller',),)
是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(是的,这很糟糕,因为没有通用的修复方法)。
解决方案 7:
还有一种情况可能比较罕见。
如果您首先在 mysqlworkbench 中创建一个模式,您将收到编码错误,并且无法通过添加字符集配置来解决它。
这是因为mysqlworkbench默认使用latin1创建模式,所以你应该首先设置字符集!
解决方案 8:
和 db.set_character_set('utf8'),暗示 use_unicode=True ?
扫码咨询,免费领取项目管理大礼包!