最简单的方法是将符号作为字符串来处理:
>>> 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(注意我甚至没有
真的?
妥善处理是一种巨大的痛苦。