unicode Python字符串中的字节

Eti*_*rot 33 python unicode utf-8 character-encoding

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

a = u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'
Run Code Online (Sandbox Code Playgroud)

我知道这绝对不是应该在他自己的代码中编写的东西,但这是我必须处理的字符串.

上面字符串中的字节是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'
Run Code Online (Sandbox Code Playgroud)

然后从UTF-8解码后给出了包含字节的初始字符串,这不好:

>>> a.encode('utf-8').decode('utf-8')
u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'
Run Code Online (Sandbox Code Playgroud)

然而,我发现了解决问题的一种黑客方法:

>>> 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!
Run Code Online (Sandbox Code Playgroud)

这很好但看起来非常h​​acky,因为它使用了eval,repr然后是unicode字符串表示的附加正则表达式.有更干净的方式吗?

Kar*_*tel 22

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

不,他们可能不会.它们包含Unicode字符.

在原始字符串中,\xd0不是UTF-8编码的一部分.它是带有代码点208的Unicode字符.u'\xd0'== u'\u00d0'.碰巧的是,repr对于在Python 2 Unicode字符串更喜欢与表示字符\x逃逸在可能(即码点<256).

没有办法查看字符串并告诉该\xd0字节应该是某些UTF-8编码字符的一部分,或者它实际上代表该Unicode字符本身.

但是,如果您假设您始终可以将这些值解释为编码值,则可以尝试编写依次分析每个字符的内容(用于ord转换为代码点整数),将字符<256解码为UTF-8,并传递字符> = 256原样.


geo*_*org 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')   
Run Code Online (Sandbox Code Playgroud)

结果:

??? utf:?? bytes:blää  
Run Code Online (Sandbox Code Playgroud)


bee*_*jay 11

问题是您的字符串实际上并未以特定编码进行编码.你的示例字符串:

a = u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439 \xd0\xb5\xd0\xba'
Run Code Online (Sandbox Code Playgroud)

将python的unicode字符串的内部表示与utf-8编码文本混合.如果我们只考虑'特殊'字符:

>>> orig = u'\u0435\u043a'
>>> bytes = u'\xd0\xb5\xd0\xba'
>>> print orig
??
>>> print bytes
ек
Run Code Online (Sandbox Code Playgroud)

但是你说,bytesutf-8编码:

>>> print bytes.encode('utf-8')
ек
>>> print bytes.encode('utf-8').decode('utf-8')
ек
Run Code Online (Sandbox Code Playgroud)

错误!但是关于:

>>> bytes = '\xd0\xb5\xd0\xba'
>>> print bytes
??
>>> print bytes.decode('utf-8')
??
Run Code Online (Sandbox Code Playgroud)

欢呼.

所以.这对我意味着什么?这意味着你(可能)解决了错误的问题.您应该问我们/试图弄清楚为什么你的字符串是以这种形式开始的,以及如何避免它/修复它之前你把它们全部混淆了.

  • @tchris:它并没有像你认为的那样"破碎".`\ xB5`只是等效`\ u00B5`的默认表示,所以实际上破解的是启发式"bytes <256"必须是UTF-8编码的". (3认同)

kev*_*kev 5

您应该将unichrs 转换为chrs,然后解码它们.

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'
Run Code Online (Sandbox Code Playgroud)
  • r'[\000-\377]*' 将匹配unichrs u'[\u0000-\u00ff]*'
  • u'\xd0\xb5\xd0\xba' == u'\u00d0\u00b5\u00d0\u00ba'
  • 您使用utf8编码字节作为unicode代码点(这是问题)
  • 我通过假装那些错误的unichars作为相应的字节来解决问题
  • 我搜索所有这些错误的unichars,并将它们转换为字符,然后解码它们.

如果我错了,请告诉我.

  • @tchrist:在Python中也是这样.有单独的`chr`和`unichr`函数的原因是前者产生"经典"ASCII字符串,而后者产生unicode字符串.在Python 3中,没有这样的命运,所有字符串都是unicode(因此,'unichr`不再存在). (3认同)
  • 仅仅因为一个字节在0x80到0xff的范围内并不意味着它是UTF-8序列的一部分.每个字节也是有效的Unicode代码点,如果您的字符串包含该范围内的实际字符,则此方法将失败. (2认同)

Mar*_*nen 5

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

  1. 匹配类似于有效UTF-8序列的Unicode字符<U + 0100(参考:RFC 3629).
  2. 将Unicode序列编码为其等效的latin-1字节序列.
  3. 使用UTF-8将序列解码回Unicode.
  4. 用匹配的Unicode字符替换原始的类似UTF-8的序列.

请注意,如果只是正确的字符彼此相邻,这仍然可以匹配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))
Run Code Online (Sandbox Code Playgroud)

产量

的u '\ u0420\u0443\u0441\u0441\u043a\u0438\u0439 \ XD0\XB5\XD0\XBA "#$%&\!'()*+, -/0123456789:;?<=> @ 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部分\ 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:;?<=> @ 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\x7f \ x80\u07ff\u0800\U00010000\U0010ffff '