【问题标题】:Python returning the wrong length of string when using special charactersPython在使用特殊字符时返回错误的字符串长度
【发布时间】:2010-02-11 19:40:36
【问题描述】:

我有一个字符串 ë́aúlt,我想根据字符位置等获取操作的长度。问题是第一个 ë́ 被计算了两次,或者我猜 ë 在位置 0 而 ´ 在位置 1。

在 Python 中是否有任何可能的方式将 ë́ 这样的字符表示为 1?

我正在将 UTF-8 编码用于输出到的实际代码和网页。

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

【问题讨论】:

  • 这在 Perl 中很容易通过它的 Unicode::GCString 模块来实现,它的方法包括标准的东西,如lengthsubstrindex,它们都在字形上操作而不是代码点或代码单元。它甚至包括columns 方法,该方法对于东亚宽/全字符以及组合字符等特别有用。这确实是您想要的,也是这些事情应该工作的方式。但是,我还没有在 Python 中找到等价物。 Perl 比 Python 拥有更灵活和更强大的 Unicode 支持,并且擅长字符串,所以你可以考虑一下。

标签: python character-encoding


【解决方案1】:

问题是第一个 ë́ 被计算了两次,或者我猜 ë 在位置 0 而 ´ 在位置 1。

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

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

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

要找出一串 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

【讨论】:

  • +1 这个答案有效。请注意,代码部分中的 ë́ 显示错误,但我认为这只是字体/浏览器问题。
  • 这不是一个通用的解决方案。您需要一种获取字素的方法,而不仅仅是代码点,而转换为 NFC 对于一般情况来说根本不够好。这在 Perl 中很容易做到,其中 Unicode::GCString 类支持 substr()、index() 等基本操作,它们都适用于字素。然后组合字符无关紧要,一切正常。但是,据我所知,Python 没有这样的模块。
【解决方案2】:

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

这里有一些例子:

>>> # 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

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

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

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

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

问候, 克里斯托夫

【讨论】:

  • 我认为这个答案突出了问题 - ea 上的重音与问题中的重音不同:)
  • 哦,你是对的。我想我在复制角色时丢失了角色……对此感到抱歉。不幸的是,似乎没有一个 unicode 字符可以代表重音。以前从未见过这样的东西(至少我知道的德国元音变音可以用两种方式写,作为单一字符和组合字符)
【解决方案3】:

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

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

问候 朱迪

【讨论】:

    【解决方案4】:

    你能做的最好的就是使用unicodedata.normalize()来分解字符,然后过滤掉重音。

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

    【讨论】:

      【解决方案5】:

      您说:我有一个字符串 ë́aúlt,我想根据字符位置等获取操作的长度。问题是第一个 ë́ 被计算了两次,或者我猜 ë 在位置 0 而 ´ 在位置 1。

      解决任何 Unicode 问题的第一步是准确了解数据中的内容;不要猜。在这种情况下,您的猜测是正确的;不会总是这样。

      “你的数据到底是什么”:使用 repr() 内置函数(除了 unicode 之外还有更多的东西)。在您的问题中显示 repr() 输出的一个有用优势是,回答者将拥有您所拥有的内容。请注意,对于某些浏览器/字体,您的文本仅显示在 4 个位置,而不是 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 的回答 :-)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-03-13
        • 1970-01-01
        • 1970-01-01
        • 2011-06-10
        • 2012-05-14
        • 1970-01-01
        相关资源
        最近更新 更多