【问题标题】:Is it possible to construct a unicode string that the utf-8 codec cannot encode?是否可以构造一个 utf-8 编解码器无法编码的 unicode 字符串?
【发布时间】:2017-05-05 00:36:59
【问题描述】:

是否可以构造utf-8编解码器无法编码的unicode字符串?

从文档 (https://docs.python.org/2/library/codecs.html) 看来,utf-8 编解码器可以用“任何语言”对符号进行编码。文档还注意到编解码器何时只能编码某些字符或只能编码基本多语言平面。我不知道这是否等同于说“不可能构造一个无法使用utf-8 编解码器转换为字节串的unicode 值”。

这是utf-8 编解码器的表条目。

编解码器别名用途

utf_8 U8、UTF、utf8 所有语言

这里的动机是我有一个实用函数,它接受一个 unicode 字符串或一个字节字符串并将其转换为一个字节字符串。当给定一个字节字符串时,它是一个空操作。除非使用非字符串类型调用此函数,否则不应抛出异常,在这种情况下,它应该以稍后捕获并记录的 TypeError 提示失败。 (如果我们尝试插入到异常消息中的项目的 repr 太大,我们仍然会遇到问题,但我们暂时忽略它。

我正在使用strict 设置,因为我希望此函数在遇到无法编码的 unicode 对象时抛出异常,但我希望这是不可能的。

def utf8_to_bytes(item):
    """take a bytes or unicode object and convert it to bytes,
    using utf-8 if necessary"""
    if isinstance(item, bytes):
        return item
    elif isinstance(item, unicode):
        return codecs.encode(item, 'utf-8', 'strict')
    else:
        raise TypeError("item must be bytes or unicode. got %r" % type(item))

【问题讨论】:

  • 我不认为我的问题是不明智的。是的,utf-8 能够编码任何有效的 unicode 代码点序列……但这并不意味着先验地不可能在 python 中构造一个不可编码的 unicode 对象。

标签: python python-2.7 unicode utf-8


【解决方案1】:

UTF-8 旨在对所有 Unicode 标准进行编码。将 Unicode 文本编码为 UTF-8 通常不会引发异常。

来自Wikipedia article on the codec

UTF-8 是一种字符编码,能够对 Unicode 定义的所有可能的字符或代码点进行编码

Python 2 UTF-8 编码没有我所知道的极端情况;非 BMP 数据和代理对的处理方式相同:

>>> import sys
>>> hex(sys.maxunicode)  # a narrow UCS-2 build
'0xffff'
>>> len(u'\U0001F525')
2
>>> u'\U0001F525'.encode('utf-8')
'\xf0\x9f\x94\xa5'
>>> u'\ud83d\udd25'
u'\U0001f525'
>>> len(u'\ud83d\udd25')
2
>>> u'\ud83d\udd25'.encode('utf-8')
'\xf0\x9f\x94\xa5'

请注意,strict 是默认编码模式。您也不需要使用codecs 模块,只需在unicode 对象上使用encode 方法即可:

return item.encode('utf-8')

在 Python 3 中,情况稍微复杂一些。解码和编码surrogate pairs受到限制;官方标准规定此类字符只能出现在 UTF-16 编码数据中,并且只能出现在低位和高位对中。

因此,您需要使用surrogatepass error handler 明确声明您希望支持此类代码点:

允许对代理代码进行编码和解码。这些编解码器通常将代理项的存在视为错误。

surrogatepassstrict 之间的唯一区别是 surrogatepass 允许您将 Unicode 文本中的任何代理代码点编码为 UTF-8。您只会在极少数情况下获得此类数据(定义为文字,或者意外地将此类代码点留在 UTF-16 中未配对,然后使用 surrogatepass 解码)。

因此,在 Python 3 中,只有当您的 Unicode 文本有可能使用 surrogatepass 解码或文字数据生成时,您才需要使用 item.encode('utf8', 'surrogatepass')绝对确定所有可能的 Unicode 值都可以编码。

【讨论】:

  • Python 中的 unicode 对象能否只存储有效的代码点?有没有办法将无效的 UCS2 字符串或 CPython 内部使用的任何编码插入 unicode 对象?我只是想了解是否存在任何可能的奇怪边缘情况,在这些情况下,我可以得到一个不可编码的 unicode 对象,从 Python 的角度来看,它仍然是一个 unicode 对象。
  • @GregoryNisbet:我目前不知道在 Python 2 中有任何方法可以做到这一点,即使 UCS2 窄版本在内部使用 UTF-16 代理。
  • @GregoryNisbet:即使您要在文字中使用代理对,编码为 UTF-8 也可以正常工作。以u'\ud861\udd37'.encode('utf-8') 为例。
  • 当我在 Python 3.5.0 中尝试 '\ud861\udd37'.encode('utf-8') 时,我得到一个错误,UnicodeEncodeError: 'utf-8' codec can't encode character '\ud861' in position 0: surrogates not allowed。这当然意味着它是@GregoryNisbet 定义的无效代码点?即使它在 2.7 中仍然有效,也没有充分的理由让移植变得困难。
猜你喜欢
  • 2011-01-01
  • 2014-06-09
  • 2011-02-27
  • 2020-07-17
  • 2019-11-10
  • 1970-01-01
  • 2018-11-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多