unicode Python字符串中的字节

时间:2012-03-23 20:05:54

标签: python unicode utf-8 character-encoding

在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!

这很好用,但由于使用evalrepr,然后使用unicode字符串表示的其他正则表达式,因此看起来非常h​​acky。有更清洁的方式吗?

6 个答案:

答案 0 :(得分:22)

  

在Python 2中,Unicode字符串可能包含unicode和bytes:

不,他们可能没有。它们包含Unicode字符。

在原始字符串中,\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
ек

但是你说,bytesutf-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代码点(这是问题
  • 我通过假装那些错误的unichars作为相应的字节来解决问题
  • 我搜索所有这些错误的unichars,并将它们转换为chars,然后解码它们。

如果我错了,请告诉我。

答案 4 :(得分:5)

你已经得到了答案,但这里有一种解读UTF-8- 之类的 Unicode序列的方法,这种方法不太可能错误地解码latin-1 Unicode序列。 re.sub函数:

  1. 匹配Unicode字符&lt; U + 0100类似于有效的UTF-8序列(参考:RFC 3629)。
  2. 将Unicode序列编码为等效的latin-1字节序列。
  3. 使用UTF-8将序列解码回Unicode。
  4. 使用匹配的Unicode字符替换原始的类似UTF-8的序列。
  5. 请注意,如果只是正确的字符彼此相邻,这仍然可以匹配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")