【问题标题】:Django: Unicode, MySQL and Encodings (latin1, koi8-r)Django:Unicode、MySQL 和编码(latin1、koi8-r)
【发布时间】:2018-08-18 05:24:51
【问题描述】:

Django 2.0 版。 Python 3

我的数据库字符集和排序规则:

mysql> SELECT @@character_set_database, @@collation_database;
+--------------------------+----------------------+
| @@character_set_database | @@collation_database |
+--------------------------+----------------------+
| latin1                   | latin1_swedish_ci    |
+--------------------------+----------------------+

老开发者使用 Perl 以KOI8-R 编码插入数据:(

为了从数据库中获取正确的值,我使用了丑陋的构造 str(username).encode('latin1').decode('koi8-r')。什么?我需要在我的所有项目中使用它来将数据发送到输出?或者编写函数来编码上下文字典,但我还需要额外的编码/解码所有数据。它会影响可用性和生产力

没有这个我会得到类似ëÏÚÌÑÎËÏ òÏÍÁÎ éÏÓÉÆÏ×ÉÞ

如何在 Django 中全局设置编码以防止在每个地方进行编码/解码操作?我更改了不同的编码方式,但没有任何反应。

在 settings.py 中,我尝试将 DEFAULT_CHARSET 设置为不同的编码(如果我将 default_charset 设置为 KOI8-R,则会出现错误:UnicodeEncodeError: 'charmap' codec can't encode characters in position 6228-6235: character maps to .使用其他编码没有错误但没有结果)。我试图在 settings.py 的数据库部分设置不同的字符集和排序规则值。

'OPTIONS': {
    'charset': 'latin1',
    'init_command': "SET sql_mode='STRICT_TRANS_TABLES', character_set_client=latin1, character_set_results=latin1, character_set_connection=latin1, collation_connection=latin1_swedish_ci",
}

我在 index.html 模板中将 <meta http-equiv="Content-type" content="text/html; charset=koi8-r (or other)" /> 添加到 <head> 标记。没有结果。

似乎 Django 每次都执行SET NAMES utf8

为什么在 Perl 中我可以发送带有 charset=koi8-r 的标头,而我使用 CGI 从浏览器中的这些表中获取正常值?为什么使用 Django 或 Flask 在 Python 中没有类似的结果? Simple example in Perl

【问题讨论】:

标签: python mysql django encoding character-encoding


【解决方案1】:

我认为您将网络字符编码与存储编码混淆了。 MySQL中字符串数据的生命周期大致是这样的:

disk_storage --decode--> MySQL --encode--> network --decode--> database_driver

当从磁盘读取字符串数据时,MySQL 使用character_set_database 值对其进行解码。 当客户端通过网络连接时,客户端指定连接的编码。对于 Python,这通常是 UTF-8。 MySQL 然后将数据编码为连接编码。 然后,Python Mysql 驱动程序使用它设置的连接编码解码它接收到的数据。

如果这些解码或编码中的任何一个使用了错误的值,则会创建错误的数据。如果character_set_database设置不正确,那么MySQL将在对网络连接上的坏数据进行编码之前错误地解码数据。

解决方案应该很简单,只需将character_set_database 更改为正确的值无需更改实际数据

这可以通过以下方式实现:

ALTER DATABASE dbname CHARACTER SET koi8r COLLATE koi8r_general_ci;

(请勿运行ALTER TABLE tbl_name CONVERT.. - 这实际上会重新编码您的数据。由于旧的 character_set 值错误,您的数据在编码为新编码之前将被错误解码)

将所有 Python 设置改回默认设置(UTF-8 等)。不要设置DEFAULT_CHARSET 或任何其他值。

为确保 MySQL 驱动程序正确连接并使用 UTF-8 进行网络连接,请设置 use_unicode=Truecharset="utf8"

例如

>>> db = MySQLdb.connect(host="localhost", user='root', passwd='passwd', db='sandbox', use_unicode=True, charset="utf8")

【讨论】:

  • 感谢您的回复。我早些时候尝试过这样做 - 没有效果。将数据库甚至表的字符集和排序规则更改为 koi8r。 MySQLdb中连接的简单示例: con = mdb.connect('localhost', 'user', 'passwd', 'database');我得到 þÅÐÒÑÇÏ×Á éÎÅÓÓÁ ÷ÉËÔÏÒÏ×ÎÁ 输出。这没有 Django!
  • 所以你跑了ALTER TABLE tbl_name CONVERT TO CHARACTER SET koi8r COLLATE koi8r_general_ci;?
  • 如果全部默认mysql> ALTER TABLE user CONVERT TO CHARACTER SET koi8r COLLATE koi8r_general_ci; ERROR 1366 (HY000): Incorrect string value: '\xF0\xCF\xC7\xCF\xCE\xC9...' for column 'fio' at row 1. 如果我在更改表用户字符集 koi8r collat​​e koi8r_general_ci;在此之前 - 结果相同(字符串值不正确)
  • 不-您想多了,没有阅读我的答案。如果您创建一个新数据库并导入,那么您可能会再次错误地解码和重新编码。将所有 Python 设置恢复为默认值。按原样从备份还原数据库。除了运行 ALTER DATABASE dbname CHARACTER SET koi8r COLLATE koi8r_general_ci; 之外不要更改任何内容
  • 更正:为确保 MySQL 驱动程序正确连接,请在您的连接上设置 use_unicode=Truecharset="utf8"。例如。 db = MySQLdb.connect(host="localhost", user='root', passwd='passwd', db='sandbox', use_unicode=True, charset="utf8")
【解决方案2】:

我解决了这个问题。

第一种方式

ALTER DATABASE dbname CHARACTER SET koi8r COLLATE koi8r_general_ci;

use_unicode=True, charset="utf8" 属性关联。 但我无法更改服务器上的活动数据库,只能在测试 PC 上更改。

第二种方式

在我的虚拟环境中,我编辑了 lib/python3.6/site-packages/MySQLdb/connections.py:

Connection 类中我添加了:

self.force_koi8r = kwargs2.pop('force_koi8r', kwargs.get('force_koi8r', False))

及以下编辑部分代码:

def set_character_set(self, charset):
    #if charset == "utf8mb4":
    #    py_charset = "utf8"
    #else:
    #    py_charset = charset

    # bugfix:
    if charset == "utf8mb4":
        py_charset = "utf8"
    elif charset == "latin1" and self.force_koi8r == True:
        py_charset = 'koi8-r'
    else:
        py_charset = charset

现在它可以与 force_koi8r=True 参数一起正常工作,但这不是一个好的解决方案。此解决方案仅适用于本项目

问题在于编码的标题。 Python 将此编码称为“koi8-r”或“koi8_r”。但 MySQL 将其称为“koi8r”。如果我设置 charset=koi8-r - mysql 会出错,如果我设置 charset=koi8r - Python 会出错。 (未知编码)

第三种方式

如果有人知道如何在 Python 中添加编码别名(默认:koi8-r、koi8_r,我需要添加 koi8r)请告诉我

【讨论】:

    【解决方案3】:

    我很确定您需要 2-step ALTER:

    您有 CHARACTER SET latin1,但有非 latin1 字节。在修复字符集时,您需要单独保留字节:

    首先,假设您有 tbl.col 的声明:

    col VARCHAR(111) CHARACTER SET latin1 NOT NULL
    

    在不改变字节的情况下转换列:

    ALTER TABLE tbl MODIFY COLUMN col VARBINARY(111) NOT NULL;
    ALTER TABLE tbl MODIFY COLUMN col VARCHAR(111) CHARACTER SET koi8r NOT NULL;
    

    注意:如果以 TEXT 开头,请使用 BLOB 作为中间定义。 (请务必保持其他规范相同 - VARCHAR、NOT NULL 等)

    -- http://mysql.rjweb.org/doc.php/charcoll#fixes_for_various_cases

    正如您将在该链接中看到的那样,针对不同的场景有许多不同的修复。如果您已经应用了其他一些答案,您可能会让事情变得更糟!如果您需要进一步的帮助,请提供示例

    SELECT col, HEX(col) FROM ... WHERE ...
    

    可能能够推断出事情的位置。 ëÏÚÌÑÎËÏ òÏÍÁÎ éÏÓÉÆÏ×ÉÞþÅÐÒÑÇÏ×Á éÎÅÓÓÁ ÷ÉËÔÏÒÏ×ÎÁ 看起来完全被破坏了;快速浏览一下可能是应用了错误的转换。韩文原文是什么?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-15
      • 1970-01-01
      • 2018-08-20
      • 2014-08-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多