【问题标题】:Writing UTF-8 String to MySQL with Python使用 Python 将 UTF-8 字符串写入 MySQL
【发布时间】:2011-09-06 08:38:30
【问题描述】:

我正在尝试将用户帐户数据从 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

【问题讨论】:

    标签: python unicode utf-8


    【解决方案1】:

    假设您使用的是 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 库有一种自然的偏执狂,所以我尽量明确以使错误更容易找到,如果图书馆改变了。

    【讨论】:

    • 是的,我正在使用 MySQLdb。在我之前的帖子中忘记提到了。我尝试手动设置 charset 和 use_unicode (虽然前者似乎暗示后者),但结果是一样的。我还尝试在 INSERT-Statement 中设置 .decode("utf-8") 。还是一样的格式...
    • 数据库中列的编码设置是什么?试试 utf8-bin。您可能正在完美正确地传输数据,但它是以不包含您正在使用的字符的某种编码编写的。
    • 编码为utf8_general_ci,我尝试将表和列设置为utf8_bin,但没有效果。
    • 更新以反映我测试后的结果。
    • 我认为这是正确的方向,我尝试手动设置字符串,这似乎有效。我认为我在 python 中的转换似乎有问题。在接下来的几天里,我将继续调试它,并尽可能发布解决方案。顺便说一句:感谢您的测试!
    【解决方案2】:

    正如@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

    【讨论】:

    • 对于所有 MySQLdb.execute 语句都是如此 - 您必须像这样将 decode('utf8') 解码为 un​​icode 并以字符串形式提交?然后我的测试结果在数据库中是 unicode,而不是 utf8 编码,但是如果我尝试直接提交 unicode,我会得到 latin1 废话。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      和 db.set_character_set('utf8'),暗示 使用_unicode=真?

      【讨论】:

      • 抱歉回答迟了:是的,字符集暗示 use_unicode = True
      【解决方案5】:

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

      这种情况下没有得到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 - 或 [anything]_bin...)你必须期待“原始”值,这里是 utf-8(是的,这很糟糕,因为没有通用修复)。

      【讨论】:

        【解决方案6】:
        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
        

        【讨论】:

        • 这是唯一真正包含所有可能的 Unicode 字符,甚至是表情符号的答案。谢谢。
        【解决方案7】:

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

        概述

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

        MySQLdb 尊重游标描述字段类型,因此如果游标中有 DATETIME 列,则值将转换为 Python datatime.datetime 实例,DECIMALdecimal.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 &amp; 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 解码器。所以你也想尝试转换二进制值,你可以弹出第一个元组。

        【讨论】:

          【解决方案8】:

          还有另一种情况可能有点少见。

          如果先在mysqlworkbench中创建schema,会出现编码错误,无法通过添加charset配置解决。

          因为mysqlworkbench默认是按latin1创建schema的,所以要先设置charset!

          【讨论】:

            猜你喜欢
            • 2023-03-27
            • 2015-02-23
            • 2017-05-14
            • 2014-06-01
            • 2011-06-16
            • 2013-01-26
            • 2016-02-17
            • 2011-04-21
            • 2011-07-21
            相关资源
            最近更新 更多