Python反转一个UTF-8字符串

8

我目前正在学习Python,并且作为斯洛文尼亚人,我经常使用UTF-8字符来测试我的程序。通常一切都正常,但有一个小问题我无法解决。尽管我在文件顶部声明了编码方式,但当我尝试反转包含特殊字符的字符串时,它仍然失败。

#-*- coding: utf-8 -*-

a = "čšž"
print a    #prints čšž
b = a[::-1]
print b    #prints �šō� instead of žšč

有没有办法解决这个问题?
1个回答

16

Python 2字符串为字节串,并且UTF-8编码的文本使用多个字节表示一个字符。仅仅因为你的终端可以将UTF-8字节解释为字符,并不意味着Python知道哪些字节组成一个UTF-8字符。

你的字节串由6个字节组成,每两个字节形成一个字符:

>>> a = "čšž"
>>> a
'\xc4\x8d\xc5\xa1\xc5\xbe'

然而,UTF-8 使用多少字节取决于字符在 Unicode 标准中定义的位置;ASCII 字符(Unicode 标准中的前 128 个字符)只需要 1 个字节,许多表情符需要 4 个字节!

在 UTF-8 中,顺序是 一切;反转上述字节串会翻转字节,就 UTF-8 标准而言会导致一些无用信息,但其中间的 4 个字节恰好是有效的 UTF-8 序列(对应 šō):

>>> a[::-1]
'\xbe\xc5\xa1\xc5\x8d\xc4'
-----~~~~~~~~^^^^^^^^####
  |     š       ō      |
  \                    \
   invalid UTF8 byte    opening UTF-8 byte missing a second byte

您需要将字节字符串解码为unicode对象,该对象由单个字符组成。反转该对象即可得到正确的结果:

b = a.decode('utf8')[::-1]
print b

你可以始终将对象重新编码为UTF-8:
b = a.decode('utf8')[::-1].encode('utf8')

请注意,在Unicode中,当使用组合字符时,反转文本仍可能会遇到问题。反转包含组合字符的文本会将这些组合字符放在字符之前而不是之后,因此它们会与错误的字符组合:

>>> print u'e\u0301a'
éa
>>> print u'e\u0301a'[::-1]
áe

通过将Unicode数据转换为其规范化形式(替换组合为1码点形式),您可以大多数避免此问题,但还有许多其他奇异的Unicode字符与字符串反转不兼容。


只是澄清一下:“但还有很多其他奇特的Unicode字符与字符串反转没有交互作用”是指“与字符串反转不起作用”还是“不受字符串反转的影响”? - Piovezan
@Piovezan: 我自己也不是100%确定;我会选择不能很好地处理字符串反转 - Martijn Pieters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接