如何在Python中分割前面有数字的utf-8字符串?

2 投票
2 回答
709 浏览
提问于 2025-04-18 17:28

我有一个原始的utf-8字符串:

u'1\u670d-\u82f1\u96c4\u96c6\u7ed3'

然后我把它转换成字符串

s = str(u'1\u670d-\u82f1\u96c4\u96c6\u7ed3'.encode('utf8'))
print s
'1\xe6\x9c\x8d-\xe8\x8b\xb1\xe9\x9b\x84\xe9\x9b\x86\xe7\xbb\x93'    

我想把那个数字1和其他字符串分开。

于是我试了试:

s.split('\\')
['1\xe6\x9c\x8d-\xe8\x8b\xb1\xe9\x9b\x84\xe9\x9b\x86\xe7\xbb\x93']

s.split('\\x')
['1\xe6\x9c\x8d-\xe8\x8b\xb1\xe9\x9b\x84\xe9\x9b\x86\xe7\xbb\x93']

结果并不是我想的那样。

最后我灵光一闪,做了:

s.split('\xe6')
['1', '\x9c\x8d-\xe8\x8b\xb1\xe9\x9b\x84\xe9\x9b\x86\xe7\xbb\x93']

但问题是,我不能保证其他类似的utf-8编码会以'\xe6'开头,所以我需要一种方法来区分数字和任意的utf-8编码,然后把它们分开。

这可能实现吗?

2 个回答

2

小心了,你给我们的所谓“原始utf-8字符串”其实和这个完全不一样。

首先最重要的是,虽然这应该放在最后说:你字符串的最终解码显示出一个有效的中文短语,并且在谷歌翻译中得到了一个看似有效的翻译:“1 英雄构建”:

enter image description here

第二件事:现在停止你正在做的任何事情,去看看Joel on Software上关于Unicode的经典文章。真的,这对你会有帮助,不要因为标题而觉得被嘲笑了,去读一读。

第三,现在让我们来看看你的数据有什么问题:你那里的是一个Python的Unicode对象,而不是“原始utf-8字符串”。当你正确地将这个特定的字符串编码为utf-8时,你会得到一个字符串(字节)对象,它的内容是 '1\xe6\x9c\x8d-\xe8\x8b\xb1\xe9\x9b\x84\xe9\x9b\x86\xe7\xbb\x93'。如果你把它放在一个 s 变量中,并且获取索引为1的字符,正如你发现的,你会得到一个斜杠(\)字符,也就是说,你在这里展示的斜杠并不是用作转义序列的一部分来编码字节“0xe6, 0x9c”等等……它们实际上就是在那里。它的 repr'1\\xe6\\x9c\\x8d-\\xe8\\x8b\\xb1\\xe9\\x9b\\x84\\xe9\\x9b\\x86\\xe7\\xbb\\x93'

然而,它们应该只是转义序列的一部分,所以你需要做的是将你的(字节)字符串 s 解码回Unicode,但要使用特殊的“unicode_escape”编码器——这样你就会得到一个Unicode对象,其中的斜杠实际上是前面字节代码的转义字符:

>>> print s, repr(s)
1\xe6\x9c\x8d-\xe8\x8b\xb1\xe9\x9b\x84\xe9\x9b\x86\xe7\xbb\x93 '1\\xe6\\x9c\\x8d-\\xe8\\x8b\\xb1\\xe9\\x9b\\x84\\xe9\\x9b\\x86\\xe7\\xbb\\x93'
>>> s1 = s.decode("unicode_escape")
>>> print repr(s1)
u'1\xe6\x9c\x8d-\xe8\x8b\xb1\xe9\x9b\x84\xe9\x9b\x86\xe7\xbb\x93'

这个Unicode对象作为Unicode是没有意义的,但它的原始字节内容才是utf-8中的实际最终字符串:

>>> print s1
1æ-è±ééç»

由于巧合,“latin-1”转换并不是因为“它就是这样”,而是将Unicode对象中的字节直接转置为普通(字节)字符串。这并不是“Unicode的事情”——只是因为Python的Unicode对象内部表示的值0-255恰好与latin-1编码匹配,所以在“latin-1”中编码提供了“透明”的编码。标准中并没有规定这一点。当这个字符串以UTF-8的形式查看时(注意,我在utf-8控制台中使用Python交互式解释器——你看到的可能在使用latin-1或其他编码的Python提示符下有所不同),显示出在这种情况下很可能是预期的字符串:

>>> s2 = s1.encode("latin-1")

>>> print repr(s2)
'1\xe6\x9c\x8d-\xe8\x8b\xb1\xe9\x9b\x84\xe9\x9b\x86\xe7\xbb\x93'

>>> print s2
1服-英雄集结
3

如果它总是一个单独的数字,那就直接取第一个项目就可以了:

digit = s[0]

否则,你可以用正则表达式来扫描它:

number = re.match(r'^\d+', s).group(0)

撰写回答