代码之家  ›  专栏  ›  技术社区  ›  smaug

如何解释Python中的Unicode符号?

  •  1
  • smaug  · 技术社区  · 6 年前

    如何转换形式Unicode符号 'U+1F600' 变成这样: '\U0001F600' ,我在网上的网站上看到的是“python-src”?

    我的最终目标是在python(2.x)中使用Unicode来表示emojis,我可以通过以下方式实现它:

    unicode_string = '\U0001F600'
    unicode_string.decode('unicode-escape')
    

    如果您能提到上述问题中涉及的不同字符集,我将不胜感激。

    1 回复  |  直到 6 年前
        1
  •  4
  •   abarnert    6 年前

    最简单的方法是将符号作为字符串来处理:

    >>> s = 'U+1F600'
    >>> s[2:] # chop off the U+
    '1F600'
    >>> s[2:].rjust(8, '0') # pad it to 8 characters with 0s
    '0001F600'
    >>> r'\U' + s[2:].rjust(8, '0') # prepend the `\U`
    '\\U0001F600'
    

    将字符串解析为十六进制,然后将得到的数字重新格式化可能会更干净一些:

    >>> int(s[2:], 16)
    128512
    >>> n = int(s[2:], 16)
    >>> rf'\U{n:08X}'
    '\\U0001F600'
    

    __,但我不确定这真的更容易理解。


    如果需要从更大的字符串中提取这些内容,您可能需要一个正则表达式。

    我们要匹配一个文本 U+ 后跟1到8个十六进制数字,对吗?所以,那是 U\+[0-9a-fA-F]{1,8} . 但我们真的不需要包括 u+ 只是为了把它拉下来 [2:] ,那么让我们将它的其余部分分组: U\+([0-9a-fA-F]{1,8}) .

    >>> s = 'Hello U+1F600 world'
    >>> re.search(r'U\+([0-9a-fA-F]{1,8})', s)
    <_sre.SRE_Match object; span=(6, 13), match='U+1F600'>
    >>> re.search(r'U\+([0-9a-fA-F]{1,8})', s).group(1)
    '1F600'
    

    现在,我们可以使用 re.sub 具有应用 \U 准备和 rjust 衬垫:

    >>> re.sub(r'U\+([0-9a-fA-F]{1,8})', lambda match: r'\U' + match.group(1).rjust(8, '0'), s)
    'Hello \\U0001F600 world'
    

    如果您不按行定义函数,那么它的可读性可能更高:

    >>> def padunimatch(match):
    ...     return r'\U' + match.group(1).rjust(8, '0')
    >>> re.sub(r'U\+([0-9a-fA-F]{1,8})', padunimatch, s)
    'Hello \\U0001F600 world'
    

    或者,如果您喜欢用数字来表示:

    >>> def padunimatch(match):
    ...     n = int(match.group(1), 16)
    ...     return rf'\U{n:08X}'
    >>> re.sub(r'U\+([0-9a-fA-F]{1,8})', padunimatch, s)
    'Hello \\U0001F600 world'
    

    当然,你已经知道如何完成最后一部分了,因为这是你的问题,对吧?嗯,不完全是:你不能打电话 decode 在字符串上,仅在 bytes . 最简单的方法是直接使用编解码器:

    >>> x = 'Hello \\U0001F600 world'
    >>> codecs.decode(x, 'unicode_escape')
    'Hello 😀 world'
    

    除非你使用的是python 2。在这种情况下, str 类型不是Unicode字符串,而是字节字符串,因此 译码 实际上工作得很好。但是在Python2中,您会遇到其他问题,除非您的所有文本都是纯ASCII(任何非ASCII字符编码为 U+xxxx 序列)。

    例如,假设您的输入是:

    >>> s = 'Hej U+1F600 världen'
    

    在Python3中,这很好。那 s 是Unicode字符串。在封面下,我的控制台将python utf-8编码的字节发送到标准输入,并期望从标准输出中得到utf-8编码的字节,但这就像魔术一样工作。(好吧,不是很神奇)你可以 print(sys.stdin.encoding, sys.stdout.encoding) 要知道python知道我的控制台是utf-8,并代表我使用它进行解码和编码。)

    在Python2中,它不是。如果我的控制台是UTF-8,那么我实际所做的相当于:

    >>> s = 'Hej U+1F600 v\xc3\xa4rlden'
    

    如果我尝试将其解码为 unicode-escape ,python 2将处理这些 \xc3 \xa4 字节为拉丁-1字节,而不是UTF-8:

    >>> s = 'Hej \U0001F600 v\xc3\xa4rlden'
    

    __所以你最后得到的是:

    >>> s.decode('unicode_escape')
    u'Hej \U0001f600 v\xc3\xa4rlden'
    >>> print(s.decode('unicode_escape'))
    Hej 😀 världen
    

    但是如果你先把它解码成UTF-8,然后再解码怎么办? 那个 作为Unicode转义?

    >>> s.decode('utf-8')
    u'Hej \\U0001F600 v\xe4rlden'
    >>> print(s.decode('utf-8'))
    Hej \U0001F600 världen
    >>> s.decode('utf-8').decode('unicode-escape')
    UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 16: ordinal not in range(128)
    

    与python 3不同,它只是不允许您调用 译码 在unicode字符串中,python 2允许您这样做,但它通过尝试 encode 先到ASCII,所以它有一些 译码 很明显,在这里失败了。

    您不能像在python 3中那样直接使用codec:

    >>> codecs.decode(s.decode('utf-8'), 'unicode_escape')
    UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 16: ordinal not in range(128)
    

    您可以解码utf-8,然后unicode转义结果,然后un-unicode转义所有内容,但即使这样也不完全正确:

    >>> print(s.decode('utf-8').encode('unicode_escape').decode('unicode_escape'))
    Hej \U0001F600 världen
    

    为什么?因为 Unicode逃生 在修复现有的Unicode字符的同时,也转义了反斜杠!

    如果你知道你肯定没有 u 在您不希望解析的原始源代码中进行转义,有一个快速修复方法:仅 replace 转义反斜杠:

    >>> print(s.decode('utf-8').encode('unicode_escape').replace(r'\\U', r'\U').decode('unicode_escape'))
    Hej 😀 världen
    

    如果这一切看起来像是巨大的痛苦,那么,是的,这就是为什么python 3存在的原因,因为在python 2中正确地处理unicode(注意我甚至没有 真的? 妥善处理是一种巨大的痛苦。