在Python 2中,Unicode字符串可能包含unicode和bytes:
a = u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'
我知道这绝对是不应该在他自己的代码中编写,但这是我必须处理的字符串。
上面字符串中的字节是ек
的UTF-8(Unicode \u0435\u043a
)。
我的目标是获取一个包含Unicode中所有内容的unicode字符串,即Русский ек
(\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \u0435\u043a
)。
将其编码为UTF-8会产生
>>> a.encode('utf-8')
'\xd0\xa0\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9 \xc3\x90\xc2\xb5\xc3\x90\xc2\xba'
然后从UTF-8解码,给出了包含字节的初始字符串,这不好:
>>> a.encode('utf-8').decode('utf-8')
u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'
然而,我找到了一种解决问题的方法:
>>> repr(a)
"u'\\u0420\\u0443\\u0441\\u0441\\u043a\\u0438\\u0439 \\xd0\\xb5\\xd0\\xba'"
>>> eval(repr(a)[1:])
'\\u0420\\u0443\\u0441\\u0441\\u043a\\u0438\\u0439 \xd0\xb5\xd0\xba'
>>> s = eval(repr(a)[1:]).decode('utf8')
>>> s
u'\\u0420\\u0443\\u0441\\u0441\\u043a\\u0438\\u0439 \u0435\u043a'
# Almost there, the bytes are proper now but the former real-unicode characters
# are now escaped with \u's; need to un-escape them.
>>> import re
>>> re.sub(u'\\\\u([a-f\\d]+)', lambda x : unichr(int(x.group(1), 16)), s)
u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \u0435\u043a' # Success!
这很好用,但由于使用eval
,repr
,然后使用unicode字符串表示的其他正则表达式,因此看起来非常hacky。有更清洁的方式吗?
答案 0 :(得分:22)
不,他们可能没有。它们包含Unicode字符。在Python 2中,Unicode字符串可能包含unicode和bytes:
在原始字符串中,\xd0
不是UTF-8编码的一部分。它是带有代码点208的Unicode字符。u'\xd0'
== u'\u00d0'
。事实上,Python 2中的Unicode字符串的repr
更喜欢用\x
表示尽可能转义的字符(即代码点<256)。
没有办法查看字符串并告诉\xd0
字节应该是某些UTF-8编码字符的一部分,或者它实际上代表那个Unicode字符本身。
但是,如果您假设您始终可以将这些值解释为编码值,则可以尝试编写依次分析每个字符的内容(使用ord
转换为代码点整数),解码字符&lt ; 256为UTF-8,并按原样传递字符&gt; = 256。
答案 1 :(得分:12)
(响应上面的评论):此代码转换看起来像utf8的所有内容并保留其他代码点:
a = u'\u0420\u0443\u0441 utf:\xd0\xb5\xd0\xba bytes:bl\xe4\xe4'
def convert(s):
try:
return s.group(0).encode('latin1').decode('utf8')
except:
return s.group(0)
import re
a = re.sub(r'[\x80-\xFF]+', convert, a)
print a.encode('utf8')
结果:
Рус utf:ек bytes:blää
答案 2 :(得分:11)
问题是您的字符串实际上并未以特定编码进行编码。你的示例字符串:
a = u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'
将python的unicode字符串的内部表示与utf-8
编码文本混合。如果我们只考虑'特殊'字符:
>>> orig = u'\u0435\u043a'
>>> bytes = u'\xd0\xb5\xd0\xba'
>>> print orig
ек
>>> print bytes
ек
但是你说,bytes
是utf-8
编码的:
>>> print bytes.encode('utf-8')
ек
>>> print bytes.encode('utf-8').decode('utf-8')
ек
错误!但是怎么样:
>>> bytes = '\xd0\xb5\xd0\xba'
>>> print bytes
ек
>>> print bytes.decode('utf-8')
ек
乌拉。
因此。 这对我意味着什么?这意味着你(可能)解决了错误的问题。你应该问我们/试图找出你的字符串为什么以这种形式开始的原因以及如何避免它/在之前修复它你将它们全部混淆了。
答案 3 :(得分:5)
您应该将unichr
转换为chr
s,然后解码它们。
u'\xd0' == u'\u00d0'
是True
$ python
>>> import re
>>> a = u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'
>>> re.sub(r'[\000-\377]*', lambda m:''.join([chr(ord(i)) for i in m.group(0)]).decode('utf8'), a)
u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \u0435\u043a'
r'[\000-\377]*'
将匹配unichrs u'[\u0000-\u00ff]*'
u'\xd0\xb5\xd0\xba' == u'\u00d0\u00b5\u00d0\u00ba'
utf8
个编码字节作为unicode代码点(这是问题)如果我错了,请告诉我。
答案 4 :(得分:5)
你已经得到了答案,但这里有一种解读UTF-8- 之类的 Unicode序列的方法,这种方法不太可能错误地解码latin-1 Unicode序列。 re.sub
函数:
请注意,如果只是正确的字符彼此相邻,这仍然可以匹配Unicode序列,但它的可能性要小得多。
import re
# your example
a = u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'
# printable Unicode characters < 256.
a += ''.join(chr(n) for n in range(32,256)).decode('latin1')
# a few UTF-8 characters decoded as latin1.
a += ''.join(unichr(n) for n in [2**7-1,2**7,2**11-1,2**11]).encode('utf8').decode('latin1')
# Some non-BMP characters
a += u'\U00010000\U0010FFFF'.encode('utf8').decode('latin1')
print repr(a)
# Unicode codepoint sequences that resemble UTF-8 sequences.
p = re.compile(ur'''(?x)
\xF0[\x90-\xBF][\x80-\xBF]{2} | # Valid 4-byte sequences
[\xF1-\xF3][\x80-\xBF]{3} |
\xF4[\x80-\x8F][\x80-\xBF]{2} |
\xE0[\xA0-\xBF][\x80-\xBF] | # Valid 3-byte sequences
[\xE1-\xEC][\x80-\xBF]{2} |
\xED[\x80-\x9F][\x80-\xBF] |
[\xEE-\xEF][\x80-\xBF]{2} |
[\xC2-\xDF][\x80-\xBF] # Valid 2-byte sequences
''')
def replace(m):
return m.group(0).encode('latin1').decode('utf8')
print
print repr(p.sub(replace,a))
u'\ u0420 \ u0443 \ u0441 \ u0441 \ u043a \ u0438 \ u0439 \ xd0 \ xb5 \ xd0 \ xba !“#$%&安培; \'()* +, - / 0123456789:;&LT; =&GT; @ ABCDEFGHIJKLMNOPQRSTUVWXYZ [\] ^ _`ABCDEFGHIJKLMNOPQRSTUVWXYZ {|}〜\ 0x7F部分\ X80 \ X81 \ X82 \ X83 \。? X84 \ X85 \ 86 \的x87 \ X88 \ X89 \ x8a \ x8b \ x8c \ x8d \ x8e \ X8F \ X90 \ X91 \ X92 \ X93 \ X94 \ X95 \ X96 \ X97 \ X98 \ X99 \ x9a \ x9b \ x9c \ x9d \ x9e \ x9f \ XA0 \ XA1 \ XA2 \ XA3 \ XA4 \ xa5 \ xa6 \ XA7 \ xa8版权所有\ xA9 \的Xaa \ XAB \ XAC \ XAD \ XAE \ XAF \ XB0 \ XB1 \ XB2 \ XB3 \ XB4 \ XB5 \ XB6 \ XB7 \ XB8 \ xb9 \ XBA \ XBB \ XBC \ XBD \ XBE \ XBF \ XC0 \ XC1 \ XC2 \ XC3 \ XC4 \ XC5 \ XC6 \ xc7 \ xc8 \ xc9 \ XCA \ XCB \ XCC \ XCD \ XCE \ XCF \ XD0 \ XD1 \ XD2 \ XD3 \ XD4 \ XD5 \ XD6 \ XD7 \ XD8 \ xd9 \ XDA \ XDB \ XDC \ XDD \ XDE \ XDF \ xe0 \ XE1 \ XE2 \ XE3 \ XE4 \ xe5 \ XE6 \ XE7 \ xe8 \ xe9 \ XEA \ XEB \ XEC \固定的\ XEE \ XEF \ XF0 \ XF1 \ XF2 \ XF3 \ XF4 \ XF5 \ XF6 \ XF7 \ XF8 \ xf9 \ XFA \ XFB \ XFC \ XFD \ XFE \ XFF \ 0x7F部分< b> \ XC2 \ X80 \ XDF \ XBF \ xe0 \ XA0 \ X80 \ XF0 \ X90 \ X80 \ X80 \ XF4 \ X8F \ XBF \ XBF
u'\ u0420 \ u0443 \ u0441 \ u0441 \ u043a \ u0438 \ u0439 \ u0435 \ u043a !“#$%&安培; \'()* +, - / 0123456789:;&LT; =&GT; @ ABCDEFGHIJKLMNOPQRSTUVWXYZ [\] ^ _`ABCDEFGHIJKLMNOPQRSTUVWXYZ {|}〜\ 0x7F部分\ X80 \ X81 \ X82 \ X83 \。? X84 \ X85 \ 86 \的x87 \ X88 \ X89 \ x8a \ x8b \ x8c \ x8d \ x8e \ X8F \ X90 \ X91 \ X92 \ X93 \ X94 \ X95 \ X96 \ X97 \ X98 \ X99 \ x9a \ x9b \ x9c \ x9d \ x9e \ x9f \ XA0 \ XA1 \ XA2 \ XA3 \ XA4 \ xa5 \ xa6 \ XA7 \ xa8版权所有\ xA9 \的Xaa \ XAB \ XAC \ XAD \ XAE \ XAF \ XB0 \ XB1 \ XB2 \ XB3 \ XB4 \ XB5 \ XB6 \ XB7 \ XB8 \ xb9 \ XBA \ XBB \ XBC \ XBD \ XBE \ XBF \ XC0 \ XC1 \ XC2 \ XC3 \ XC4 \ XC5 \ XC6 \ xc7 \ xc8 \ xc9 \ XCA \ XCB \ XCC \ XCD \ XCE \ XCF \ XD0 \ XD1 \ XD2 \ XD3 \ XD4 \ XD5 \ XD6 \ XD7 \ XD8 \ xd9 \ XDA \ XDB \ XDC \ XDD \ XDE \ XDF \ xe0 \ XE1 \ XE2 \ XE3 \ XE4 \ xe5 \ XE6 \ XE7 \ xe8 \ xe9 \ XEA \ XEB \ XEC \固定的\ XEE \ XEF \ XF0 \ XF1 \ XF2 \ XF3 \ XF4 \ XF5 \ XF6 \ XF7 \ XF8 \ xf9 \ XFA \ XFB \ XFC \ XFD \ XFE \ XFF \ 0x7F部分< b> \ X80 \ u07ff \ u0800 \ U00010000 \ U0010ffff
答案 5 :(得分:0)
我解决了
unicodeText.encode("utf-8").decode("unicode-escape").encode("latin1")