【问题标题】:Unicode representation to formatted Unicode?Unicode 表示为格式化的 Unicode?
【发布时间】:2017-06-03 12:49:36
【问题描述】:

我在理解将 unicode 表达式翻译成各自的字符时遇到了一些困难。我一直在查看 unicode 规范,并且遇到了各种格式如下U+1F600 的字符串。据我所见,似乎没有内置函数知道如何将这些字符串转换为 Python 的正确格式,例如\U0001F600

在我的程序中,我创建了一个小的正则表达式,它将找到这些U\+.{5} 模式并将U+ 替换为\U000。但是,我发现对于所有 unicode 字符,这种语法并不相同,例如实际上应该从 U+200D 转换为 \u200D 的零宽度连接。

因为我不知道正确的 unicode 转义序列的每一个变体,处理这种情况的最佳方法是什么?是我只能检查有限数量的这些特殊字符还是我完全错误的方式?

Python 版本是 2.7。

【问题讨论】:

  • 对不起,我把它加进去。它是 Python 2.7。 :)
  • u'\u200D' == u'\U0000200D'
  • 谢谢!我想知道我添加的示例是否会接受额外的零。如果是这样的话,也许这可以解决非均匀错误。 - 事实证明确实如此,因为\U00001F600 是一个不同的角色。还是谢谢!

标签: python python-2.7 unicode emoji python-unicode


【解决方案1】:

U+NNNN 只是用于讨论 Unicode 的常用符号。 Python 对单个 Unicode 字符的语法是以下之一:

  • u'\xNN' 用于 U+00FF 的 Unicode 字符
  • u'\uNNNN' 用于 U+FFFF 的 Unicode 字符
  • u'\U00NNNNNN' 用于 U+10FFFF(最大)的 Unicode 字符

注意:N 是十六进制数字。

输入字符时使用正确的符号。即使对于低字符,您也可以使用较长的符号:

u'A' == u'\x41' == u'\u0041' == u'\U00000041'

您还可以通过编程方式使用unichr(n) (Python 2) 或chr(n) (Python 3) 生成正确的字符。

请注意,在 Python 3.3 之前,Python 有 narrowwide Unicode 版本。 unichr/chr 只能支持 sys.maxunicode,在窄版本中为 65535 (0xFFFF),在宽版本中为 1114111 (0x10FFFF)。 Python 3.3 统一了构建并解决了 Unicode 的许多问题。

如果您正在处理U+NNNN 格式的文本字符串,这里有一个正则表达式(Python 3)。它查找 U+ 和 4-6 个十六进制数字,并将它们替换为 chr() 版本。请注意,ASCII 字符 (Python 2) 或可打印字符 (Python 3) 将显示实际字符,而不是转义版本。

>>> re.sub(r'U\+([0-9A-Fa-f]{4,6})',lambda m: chr(int(m.group(1),16)),'testing U+1F600')
'testing \U0001f600'
>>> re.sub(r'U\+([0-9A-Fa-f]{4,6})',lambda m: chr(int(m.group(1),16)),'testing U+5000')
'testing \u5000'
>>> re.sub(r'U\+([0-9A-Fa-f]{4,6})',lambda m: chr(int(m.group(1),16)),'testing U+0041')
'testing A'
>>> re.sub(r'U\+([0-9A-Fa-f]{4,6})',lambda m: chr(int(m.group(1),16)),'testing U+0081')
'testing \x81'

【讨论】:

  • O.P.正在处理像"U+1F600" 这样的text。既然不是 Python 的语法,就需要一些解析或者转换吧?
  • 感谢您的回答。 Python 3.3 现在听起来非常好。我可能应该诚实地使用它。我事先尝试使用unichr 方法,但由于构建问题狭窄,它不起作用。我假设 Python 3 chr 方法可以处理这个问题。
  • 是的,正如@wim 所说,我正在编辑 unicode 字符的文本表达式。感谢您的详细回答! :)
  • @lindsay 添加了一个正则表达式来解析该语法,但请注意,如果您使用的是窄版本,Python 2 会出现 U+10000 - U+10FFFF 的问题。
  • @lindsay 在 Python 3.3 之前,Unicode 字符在内部使用UTF-16LE 编码在窄版本中存储,而在宽版本中使用 UTF-32LE。 UTF-16 编码对 U+10000 以下的 Unicode 代码点使用一个 16 位字,对其他所有内容使用两个 16 位字。所以有像len(u'\U0001F600') == 2len(u'\U0000FFFF') == 1 这样的怪事。 unichr() 仅支持在窄版本中返回低于 U+10000 的代码点。 @wim 的 struct.pack 技巧可以解决这个问题。
【解决方案2】:

您可以查看json 模块实现。好像没那么简单:

# Unicode escape sequence
uni = _decode_uXXXX(s, end)
end += 5
# Check for surrogate pair on UCS-4 systems
if sys.maxunicode > 65535 and \
0xd800 <= uni <= 0xdbff and s[end:end + 2] == '\\u':
    uni2 = _decode_uXXXX(s, end + 1)
    if 0xdc00 <= uni2 <= 0xdfff:
        uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
        end += 6
char = unichr(uni)

(来自 cpython-2.7.9/Lib/json/decoder.py 第 129-138 行)

我认为直接使用json.loads会更方便:

>>> print json.loads('"\\u0123"')
ģ

【讨论】:

  • 如果是 json,当然可以。但是谁说 json 呢?
  • @wim,您可以从 unicode 序列创建字符串并将它们提供给 json 解析器
【解决方案3】:

我认为您最可靠的方法是将数字解析为整数,然后使用unichr 查找该代码点:

unichr(0x1f600)  # or: unichr(int('1f600', 16))

注意: 在 Python 3 上,它只是 chr

【讨论】:

  • 感谢您的回答。可能是我,但我刚刚尝试了 Python 2.7 示例,它抛出了这个错误:unichr() arg not in range(0x10000) (narrow Python build). 有什么想法吗?
  • import unicodedata; unicodedata.unidata_version 得到什么?
  • 终端说我正在使用5.2.0。也许那已经过时了?
  • 不,没关系,这只是 python 的糟糕构建。你可以试试struct.pack('i', 0x1f600).decode('utf-32') 看看结果吗?
  • 使用struct.pack 既绝妙又恶心。您应该将其编辑到您的答案中。 @lindsay 没有理由 unichr 不能这样做,这还不够好。
猜你喜欢
  • 1970-01-01
  • 2015-05-08
  • 2021-10-29
  • 2019-03-26
  • 2010-09-07
  • 2012-04-17
  • 1970-01-01
  • 2021-03-09
相关资源
最近更新 更多