【问题标题】:How to return str from MySQL using mysql.connector?如何使用 mysql.connector 从 MySQL 返回 str?
【发布时间】:2015-02-18 09:23:26
【问题描述】:

我正在尝试将来自 mysql.com 的 MySQL Connector/Python 与 Python 3 一起使用。

我有 UTF-8 编码的表,当我获取行时,我的所有字符列都返回了 bytearray。这会造成一些混乱。

如何直接获取str

更新:

# -*- coding: utf-8 -*-
import mysql.connector
con = mysql.connector.connect( user ="root", db = "vg_site_db", charset = 'utf8' )
cursor = con.cursor()
sql = """select caption from domains
"""
cursor.execute( sql )
row = cursor.fetchone()
while row is not None:
    print( row )
    row = cursor.fetchone()

输出:

(bytearray(b'ezsp.ru'),)
(bytearray(b'eazyshop.ru'),)
(bytearray(b'127.0.0.1:8080'),)
(bytearray(b'rmsvet.ru'),)

我想要:

('ezsp.ru',)
('eazyshop.ru',)
('127.0.0.1:8080',)
('rmsvet.ru',)

UPD2:

我的表使用COLLATE utf8_bin

【问题讨论】:

  • 显示读取数据库内容的python代码
  • 我更新帖子并添加代码示例
  • 我还在 Python 2.7 中从 cursor.fetchone() 获取字节数组。通过str(row[0].decode()) 将它们传递给 Python 2.7 和 3.4 中的原生字符串。 MySQL made a change 在连接器版本 2 中。

标签: python mysql python-3.x utf-8 collation


【解决方案1】:

当您使用二进制排序规则时,似乎会发生这种情况,至少我也是如此。要将字节数组转换为 Unicode 字符串,您可以添加自定义转换器类:

class MyConverter(mysql.connector.conversion.MySQLConverter):

    def row_to_python(self, row, fields):
        row = super(MyConverter, self).row_to_python(row, fields)

        def to_unicode(col):
            if isinstance(col, bytearray):
                return col.decode('utf-8')
            return col

        return[to_unicode(col) for col in row]

sql = mysql.connector.connect(converter_class=MyConverter, host=...)

【讨论】:

  • 不幸的是,这不再有效,因为转换器现在必须从 MySQLConverterBase 继承,并且没有 row_to_python 方法,除非你自己复制粘贴它,呃。
  • @Tominator:确定吗?它仍然对我有用。此外,MySQLConverter 扩展了 MySQLConverterBase 并且仍然具有 row_to_python method,所以我不立即明白为什么它不应该工作。
【解决方案2】:

当各个列使用二进制排序规则定义时,MySQL 连接器将字符串(使用CHARVARCHARTEXT 数据类型存储)返回为bytearrays(例如utf8_bin)。您必须在值上调用 .decode() 才能获取 Python 字符串,例如:

for row in cursor:
    caption = row[0].decode()

也就是说,除非您有使用utf8_bin 的特定要求,否则最好在数据库级别使用utf8mb4 字符集和排序规则utf8mb4_unicode_ci。这将解决您的问题并允许完整的 Unicode 支持。有关详细信息,请参阅 thisthis

【讨论】:

  • 你不想推荐utf8mb_unicode_cs吗?据我了解,bin 按确切的字符代码排序/选择,cs 像普通用户所期望的那样排序,ci 不区分大小写,但在其他方面类似于 cs
  • @lucidbrot 没有utf8mb_unicode_cs 排序规则,但也许您可以使用utf8mb4_0900_as_cs。至于哪个更好,就看情况了。通常,对于alphabetical ordering,大写字母被认为与其对应的小写字母相同。
【解决方案3】:

mysql-connector-python==8.0.17 添加到 requirements.txt 为我解决了这个问题。

【讨论】:

    【解决方案4】:

    虽然投票最多的答案(@danmichaelo)确实有效,但我想提供我的版本来解决@Tominator 已经指出的主要“但是”;自定义转换器类现在必须继承 MySQLConverterBase 而不是 MySQLConverter。您不想继承MySQLConverter 的原因(即使它继承了@danmichaelo 指出的MySQLConverterBase)是它会在每个返回值上调用MySQLConverter 类中的相应转换器。这将实现您可能不想要的逻辑。

    为避免上述情况,您有两种选择。首先,您可以创建一个更高级别的函数,该函数将获取数据并在检索到行后对其进行更改。

    def get_data_from_db(cursor, sql_query):
        cursor.execute(sql)
        row = cursor.fetchone()
        while row is not None:
            row_to_return = row.decode('utf-8') if isinstance(row, bytearray) else row
            row = cursor.fetchone()
        
        return row
    

    如果您仍然想使用自定义转换器类方法,那么您应该按照文档中的建议继承MySQLConverterBasehttps://dev.mysql.com/doc/connector-python/en/connector-python-connectargs.html 这在 mysql-connector-python==8.0.26 之前有效,见下文)然后你可以扩展MySQLConverterBase.to_python 方法。

    class MyConverter(mysql.connector.conversion.MySQLConverterBase):
        def to_python(self, vtype, value):
        """Convert MySQL data type to Python"""
        if isinstance(value, bytearray):
            return value.decode('utf-8')
    
        super().to_python(vtype, value)
    

    附: MyConverter 类可用于实现自定义转换器,方法是创建名称与 MySQLConverter 类中相同的函数(在此处查找类:https://github.com/mysql/mysql-connector-python/blob/master/lib/mysql/connector/conversion.py)。例如,我希望将 TINYINT 转换为 bool,并添加了一个名为 MyConverter._TINY_to_python(self, value, desc=None) 的方法

    -- 更新mysql-connector-python==8.0.27--

    在 8.0.27 版本中,如果你创建一个继承 MySQLConverterBase 的转换器类,你可能会得到一个错误提示 “expected a bytes-like object, str found”。我不清楚为什么会发生这种情况,但我上面关于创建自定义转换器的回答似乎不再成立。相反,现在应该继承 MySQLConverter 类:

    class MyConverter(mysql.connector.conversion.MySQLConverter):
        def to_python(self, vtype, value):
        """Convert MySQL data type to Python"""
        if isinstance(value, bytearray):
            return value.decode('utf-8')
    
        super().to_python(vtype, value)
    

    【讨论】:

      【解决方案5】:

      我不认为你可以让光标返回字符串。 MySQL Connector Documentation 说他们选择返回字节数组,这样他们只需要为 Python2 和 Python3 维护一个代码库:

      使用“原始”游标,返回值是 bytearray 类型。这对于让 Python 2 和 3 返回相同的数据是必要的。

      我使用列表解析来解码行中的每个字节数组来解决这个问题:

      for row in cursor:
          type_fixed_row = tuple([el.decode('utf-8') if type(el) is bytearray else el for el in row])
          print( type_fixed_row )
      

      【讨论】:

      • raw 默认没有启用,所以我觉得这很令人费解。 documentation 表示“默认情况下,来自 MySQL 的字符串作为 Python Unicode 文字返回。”
      【解决方案6】:

      解决此问题的一种简单方法是确保您从 MySQL 表中检索“字符串”。为此,您只需在查询中添加 CAST,如下所示:

       # -*- coding: utf-8 -*-
      import mysql.connector
      con = mysql.connector.connect( user ="root", db = "vg_site_db", charset = 'utf8' )
      cursor = con.cursor()
      sql = "select CAST(caption as CHAR(50)) from domains"
      cursor.execute( sql )
      row = cursor.fetchone()
      while row is not None:
          print( row )
          row = cursor.fetchone()
      

      这应该适合你。

      【讨论】:

        猜你喜欢
        • 2016-06-13
        • 2019-05-10
        • 2021-07-10
        • 2015-01-31
        • 2022-09-23
        • 2013-12-08
        • 1970-01-01
        • 2017-03-11
        • 2021-06-16
        相关资源
        最近更新 更多