Django是否会对Unicode(utf-8?)字符串进行双重编码?

6 投票
2 回答
4645 浏览
提问于 2025-04-15 23:33

我在Django中存储和输出一个ndash字符时遇到了一些麻烦,想用UTF-8格式处理它。

我从一个API获取数据。在原始形式下,通过文本编辑器查看时,数据可能类似于:

"I love this detergent \u2013 it is so inspiring." 

(\u2013是&ndas;的HTML实体表示形式)。

如果我直接从API获取这个数据并在Django中显示,没问题。它在我的浏览器中显示为一个长横线。不过,我注意到如果我在视图中对这个文本进行一些操作时,需要使用decode('utf-8'),否则会出现“'ascii'编码无法编码字符”的错误。根据Django调试工具,文本传递到模板时是这样的:“I love this detergent\u2013 it is so inspiring.”

但是,当我把它存储到MySQL中,并通过同一个视图和模板读取输出时,它的显示结果变成了:

"I love this detergent – it is so inspiring"

我的MySQL表设置为DEFAULT CHARSET=utf8

现在,当我通过设置为UTF-8的终端中的MySQL监视器读取数据库中的数据时,它显示为:

"I love this detergent – it is so inspiring" 

(正确 - 显示为ndash)

当我在Python shell中使用mysqldb时,这一行是:

"I love this detergent \xe2\x80\x93 it is so inspiring" 

(这是ndash的正确UTF-8表示)

但是,如果我运行python manage.py shell,然后

In [1]: import myproject.myapp.models ThatTable
In [2]: msg=ThatTable.objects.all().filter(thefield__contains='detergent')
In [3]: msg
Out[4]: [{'thefield': 'I love this detergent \xc3\xa2\xe2\x82\xac\xe2\x80\x9c it is so inspiring'}]

我发现Django把\xe2\x80\x93当作三个独立的字符来处理,并将其编码为UTF-8,变成了\xc3\xa2\xe2\x82\xac\xe2\x80\x9c。这在显示时变成了–,因为\xe2显示为â,\x80显示为€,等等。我检查过,这确实是发送到模板中的内容。

不过,如果在Python中用decode('utf-8')解码这个长序列,结果是\xe2\u20ac\u201c,在浏览器中也显示为–. 再次尝试解码会导致UnicodeDecodeError错误。

据我所知,我已经遵循了Django关于Unicode的建议(配置了MySQL)。

有没有什么建议可以帮我找出我可能配置错误的地方?

补充说明:似乎这个问题在其他地方或系统中也出现过。当我搜索\xc3\xa2\xe2\x82\xac\xe2\x80\x9c时,发现了一个脚本,用来“修复坏的UTF8实体”,也在一个WordPress的RSS导入插件中找到。它只是将这个序列替换为–。不过,我想以正确的方式解决这个问题!

哦,我使用的是Django 1.2和Python 2.6.5。

我可以用PHP/PDO连接到同一个数据库,并且可以正常打印出这些数据,没做任何特别的处理,显示效果很好。

2 个回答

0

我在我的PHP数据插入过程中加了一个命令 set names utf8,结果在Python的命令行里,那个让人头疼的短横线变成了 \x96。不过通过Django读取和输出的时候,这个问题就解决了。

这里有个不寻常的情况,就是我通过PHP插入数据。Django会自动执行 set names utf8,所以如果我是通过Django来插入和读取数据,这个问题可能就不会出现了。我想PHP默认是用latin1编码的。

有趣的是,以前我可以从PHP读取数据,浏览器里显示得很正常,但现在短横线显示成了�,除非我在读取数据之前先调用 set names

现在一切正常了,我希望我永远都不用去理解之前发生的那些事情!

1

这看起来像是双重编码的问题。我对Python不太熟悉,但你可以试着按照这个链接的建议调整MySQL的连接设置:http://tahpot.blogspot.com/2005/06/mysql-and-python-and-unicode.html

我猜测发生的情况是,连接使用的是latin1编码,所以MySQL在存储到UTF-8字段之前又试图对字符串进行一次编码。这里的代码,特别是这一部分:

编辑:在用Python建立数据库连接时,添加以下标志:init_command='SET NAMES utf8'。

另外,在MySQL的my.cnf文件中设置:default-character-set=utf8

这可能就是你需要的设置。

撰写回答