【问题标题】:UnicodeDecodeError using Django and format-stringsUnicodeDecodeError 使用 Django 和格式字符串
【发布时间】:2018-03-25 09:55:30
【问题描述】:

我写了一个关于这个问题的小例子,让大家看看使用 Python 2.7 和 Django 1.10.8 发生了什么

# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, unicode_literals, print_function

import time
from django import setup
setup()
from django.contrib.auth.models import Group

group = Group(name='schön')

print(type(repr(group)))
print(type(str(group)))
print(type(unicode(group)))

print(group)
print(repr(group))
print(str(group))
print(unicode(group))

time.sleep(1.0)
print('%s' % group)
print('%r' % group)   # fails
print('%s' % [group]) # fails
print('%r' % [group]) # fails

退出,输出如下+回溯

$ python .PyCharmCE2017.2/config/scratches/scratch.py
<type 'str'>
<type 'str'>
<type 'unicode'>
schön
<Group: schön>
schön
schön
schön
Traceback (most recent call last):
  File "/home/srkunze/.PyCharmCE2017.2/config/scratches/scratch.py", line 22, in <module>
    print('%r' % group) # fails
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 11: ordinal not in range(128)

有人知道这里发生了什么吗?

【问题讨论】:

  • 您是否可以通过不将“组”对象嵌套在向量中的测试来补充您的问题?
  • @DavidBern 完成。
  • 我很难复制您的错误。但是,我遇到了同样的问题。原因是在 repr 方法中解码了“ä”。最初效果很好,直到我从 future 导入 unicode_literals。然后解决方案是简单地删除使用解码和 repr 来返回一个 unicode。
  • 您正在插入包含隐式解码的 unicode 字符串。请改用b'...' 字节串。
  • @DavidBern:它很容易重现:u'%s' % '&lt;Group: sch\xc3\xb6n&gt;'。这里的问题是 OP 使用的 from __future__ import unicode_literals

标签: python django format-string


【解决方案1】:

我认为真正的问题在于 django 代码。

六年前的报道:

https://code.djangoproject.com/ticket/18063

我认为 django 的补丁可以解决它:

def __repr__(self):
    return self.....encode('ascii', 'replace')

我认为 repr() 方法应该返回“7 bit ascii”。

【讨论】:

  • @TechJS 一个占位符,用于在此上下文中不重要的内容。
  • 不确定 Django 是不是错了。与 Python 2 中的 __str__ 一样,不需要返回 ASCII 安全数据。当然,核心 Python 类型可以做到这一点,但这不是规定的要求。
  • @GhostlyMartijn 是的,你是对的。我检查了文档。没有官方要求,但我称之为“最佳实践”。或“避免混淆”。文档:docs.python.org/2/reference/datamodel.html#object.__repr__
  • 好吧,这里的原因仍然是OP混合了字节串和unicode字符串。如果字符串是 ASCII 安全的,事情就会发生,但你真的不应该混合它们。 Python 3 将完全阻止这种情况。
【解决方案2】:

我不熟悉 Django。您的问题似乎是在 ASCI 中表示文本数据,实际上是 unicode。请尝试 Python 中的 unidecode 模块。

from unidecode import unidecode
#print(string) is replaced with 
print(unidecode(string))

参考Unidecode

【讨论】:

  • 这不是必需的。该模块非常适合编码为仅限于 ASCII 的目标,但这里不是这种情况。他们已经编码字节,这里的问题是隐式解码回unicode。这个模块在那里无济于事。
【解决方案3】:

如果是这种情况,那么我们需要用我们自定义的方法覆盖 unicode 方法。试试下面的代码。它会起作用的。我已经测试过了。

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

from django.contrib.auth.models import Group

def custom_unicode(self):
    return u"%s" % (self.name.encode('utf-8', 'ignore'))
Group.__unicode__ = custom_unicode

group = Group(name='schön')

# Tests
print(type(repr(group)))
print(type(str(group)))
print(type(unicode(group)))

print(group)
print(repr(group))
print(str(group))
print(unicode(group))

print('%s' % group)
print('%r' % group)  
print('%s' % [group])
print('%r' % [group])

# output:
<type 'str'>
<type 'str'>
<type 'unicode'>
schön
<Group: schön>
schön
schön
schön
<Group: schön>
[<Group: schön>]
[<Group: schön>]

参考:https://docs.python.org/2/howto/unicode.html

【讨论】:

  • 模型“组”不是我的代码。它来自 django。我不能修改它。
  • @guettli 我已经更新了代码并在 ubuntu 中对其进行了测试。请检查一次。
  • 这并不能解决问题。该模型在__repr__ 中仍将包含非 ASCII 字节。但是您现在__unicode__ 结果中它们不属于中有 UTF-8 字节。无论如何你都会得到完全相同的结果,因为你弄乱了默认编码,所以整个练习毫无意义。
  • 您所做的唯一一件事就是调用setdefaultencoding(),这就像在断腿上绑一根棍子。 这是错误的解决方案。您应该改为设置断骨,也就是说,首先不要混合字节串和 Unicode 文本。
【解决方案4】:

这里的问题是您将 UTF-8 字节串插入到 Unicode 字符串中。您的'%r' 字符串是一个Unicode 字符串,因为您使用了from __future__ import unicode_literals,但repr(group)(由%r 占位符使用)返回一个字节字符串。对于 Django 模型,repr() 可以在表示中包含 Unicode 数据,使用 UTF-8 编码为字节串。此类表示不是 ASCII 安全的

对于您的具体示例,Group 实例上的repr() 会生成字节串'&lt;Group: sch\xc3\xb6n&gt;'。将其插入 Unicode 字符串会触发隐式解码:

>>> u'%s' % '<Group: sch\xc3\xb6n>'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 11: ordinal not in range(128)

请注意,我在 Python 会话中没有使用from __future__ import unicode_literals,因此'&lt;Group: sch\xc3\xb6n&gt;' 字符串不是unicode 对象,它是str 字节字符串对象!

在 Python 2 中,您应该避免混合 Unicode 和字节字符串。始终明确规范化您的数据(将 Unicode 编码为字节或将字节解码为 Unicode)。

如果您必须使用from __future__ import unicode_literals,您仍然可以使用b 前缀创建字节串:

>>> from __future__ import unicode_literals
>>> type('')   # empty unicode string
<type 'unicode'>
>>> type(b'')  # empty bytestring, note the b prefix
<type 'str'>
>>> b'%s' % b'<Group: sch\xc3\xb6n>'  # two bytestrings
'<Group: sch\xc3\xb6n>'

【讨论】:

  • 在我们可以升级到 Python 3 之前,我们需要更加小心这种字符串操作。目前,我们可能会为 django 模型安装猴子补丁:repr.encode('ascii', 'replace')。也许,您可以将此添加到您的回复中以供未来的读者阅读。
【解决方案5】:

我很难为您的问题找到通用解决方案。 __repr__() 是我理解的应该返回 str,任何改变似乎都会导致新问题的努力。

关于__repr__() 方法是在项目外部定义的事实,您可以重载方法。例如

def new_repr(self):
    return 'My representation of self {}'.format(self.name)

Group.add_to_class("__repr__", new_repr)

我能找到的唯一可行的解​​决方案是明确告诉解释器如何处理字符串。

from __future__ import unicode_literals
from django.contrib.auth.models import Group

group = Group(name='schön')

print(type(repr(group)))
print(type(str(group)))
print(type(unicode(group)))

print(group)
print(repr(group))
print(str(group))
print(unicode(group))

print('%s' % group)
print('%r' % repr(group))
print('%s' % [str(group)])
print('%r' % [repr(group)])

# added
print('{}'.format([repr(group).decode("utf-8")]))
print('{}'.format([repr(group)]))
print('{}'.format(group))

在 python 2.x 中处理字符串是一团糟。 希望这能为如何解决(这是我能找到的唯一方法)问题带来一些启示。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-20
    • 1970-01-01
    • 1970-01-01
    • 2016-03-05
    • 1970-01-01
    • 2011-08-03
    • 1970-01-01
    • 2014-09-05
    相关资源
    最近更新 更多