Python在使用特殊字符时返回错误的字符串长度

时间:2010-02-11 19:40:37

标签: python character-encoding

我有一个字符串'aúlt,我想根据字符位置获得操作的长度,依此类推。问题是第一个ë被计算两次,或者我猜ë在位置0并且'在位置1。

在Python中是否有任何可能的方法让像ë表示为1?

我正在使用UTF-8编码来输出它的实际代码和网页。

编辑:只是为什么我需要这样做的一些背景知识。我正在研究一个将英语翻译成Seneca(一种美洲原住民语言)的项目,并且显示了很多。某些单词的某些重写规则需要知道字母位置(本身和周围的字母)和其他特征,例如重音和其他变音符号。

5 个答案:

答案 0 :(得分:18)

UTF-8是一种unicode编码,它对特殊字符使用多个字节。如果您不想要编码字符串的长度,请对其进行简单解码并在len()对象上使用unicode(而不是str对象!)。

以下是一些例子:

>>> # creates a str literal (with utf-8 encoding, if this was
>>> # specified on the beginning of the file):
>>> len('ë́aúlt') 
9
>>> # creates a unicode literal (you should generally use this
>>> # version if you are dealing with special characters):
>>> len(u'ë́aúlt') 
6
>>> # the same str literal (written in an encoded notation):
>>> len('\xc3\xab\xcc\x81a\xc3\xbalt') 
9
>>> # you can convert any str to an unicode object by decoding() it:
>>> len('\xc3\xab\xcc\x81a\xc3\xbalt'.decode('utf-8')) 
6

当然,您也可以访问unicode对象中的单个字符,就像访问str对象一样(它们都是从basestring继承的,因此具有相同的方法) :

>>> test = u'ë́aúlt'
>>> print test[0]
ë

如果您开发本地化应用程序,通常最好只在内部使用unicode - 对象,通过解码您获得的所有输入。完成工作后,您可以将结果再次编码为“UTF-8”。如果你坚持这个原则,你将永远不会看到你的服务器崩溃,因为你可能会得到任何内部UnicodeDecodeError;)

PS:请注意,在Python 3中,strunicode数据类型发生了显着变化。在Python 3中,只有unicode字符串和普通字节字符串不能再混合使用。这应该有助于避免使用unicode处理常见的陷阱...

此致 克里斯托弗

答案 1 :(得分:5)

  

问题是第一个ë被计算两次,或者我猜ë在位置0并且'在位置1。

是。这就是Unicode定义代码点的方式。通常,您可以要求Python使用Unicode规范化转换字母和单独的“组合”变音标记,如U + 0301 COMBINING ACUTE ACCENT:

>>> unicodedata.normalize('NFC', u'a\u0301')
u'\xe1' # single character: á

然而,在“带有分音符和重音符号的e”中,Unicode中没有单个字符,因为世界上没有任何语言使用过字母“ë”。 (拼音音译具有“带有分音符和锐音”的音符,但不是“e”。)因此字体支持很差;它在许多情况下呈现得非常糟糕,并且在我的网络浏览器上是一个混乱的blob。

计算出一串Unicode代码点中的“可编辑点”是一项棘手的工作,需要相当多的语言领域知识。它是“复杂文本布局”问题的一部分,该领域还包括双向文本和上下文glpyh整形和连字等问题。要进行复杂的文本布局,您需要一个库,例如Windows上的Uniscribe,或一般的Pango(有一个Python接口)。

另一方面,如果你只是想在计算时完全忽略所有组合字符,你可以轻松地摆脱它们:

def withoutcombining(s):
    return ''.join(c for c in s if unicodedata.combining(c)==0)

>>> withoutcombining(u'ë́aúlt')
'\xeba\xfalt' # ëaúlt
>>> len(_)
5

答案 2 :(得分:1)

您可以做的最好的事情是使用unicodedata.normalize()来分解角色,然后过滤掉重音。

不要忘记在代码中使用unicode和unicode文字。

答案 3 :(得分:0)

您使用的是哪个Python版本? Python 3.1没有这个问题。

>>> print(len("ë́aúlt"))
6

此致 Djoudi

答案 4 :(得分:0)

你说:我有一个字符串'aúlt,我想根据字符位置获得操作的长度,等等。问题是第一个ë被计算两次,或者我猜ë在位置0并且'在位置1。

处理任何Unicode问题的第一步是确切知道数据中的内容;别猜。在这种情况下,你的猜测是正确的;它并不总是如此。

“究竟是什么在你的数据中”:使用repr()内置函数(除了unicode之外还有很多东西)。在你的问题中显示repr()输出的一个有用的优点是,回答者的确拥有你所拥有的。请注意,您的文本仅显示四个位置,而不是一些浏览器/字体显示5个 - “e”及其变音符号和“a”在一个位置被拼凑在一起。

您可以使用unicodedata.name()函数告诉您每个组件是什么。

以下是一个例子:

# coding: utf8
import unicodedata
x = u"ë́aúlt"
print(repr(x))
for c in x:
    try:
        name = unicodedata.name(c)
    except:
        name = "<no name>"
    print "U+%04X" % ord(c), repr(c), name

结果:

u'\xeb\u0301a\xfalt'
U+00EB u'\xeb' LATIN SMALL LETTER E WITH DIAERESIS
U+0301 u'\u0301' COMBINING ACUTE ACCENT
U+0061 u'a' LATIN SMALL LETTER A
U+00FA u'\xfa' LATIN SMALL LETTER U WITH ACUTE
U+006C u'l' LATIN SMALL LETTER L
U+0074 u't' LATIN SMALL LETTER T

现在阅读@ bobince的回答: - )