【问题标题】:How to get a character from a string - getting wrong character and wrong length of string如何从字符串中获取字符 - 获取错误的字符和错误的字符串长度
【发布时间】:2019-10-15 17:19:24
【问题描述】:

下面的python给出了错误的字符串长度和错误的字符。
这里有人知道吗?

>>> w ='lòng'
>>> w 
'lòng'
>>> print (w)
lòng
>>> len(w)
5
>>> for ch in w:
...     print (ch + "-") 
... 
l- 
o- 
- 
n- 
g- 
>>> 

【问题讨论】:

  • 长度应该是 4 并且 w[1] 应该是 'ò'。 Javascript 和 Visual Basic 可以工作,但 python 不行
  • Yours 是一个 unicode 字符串。用于字符串的 python len() 方法计算 代码点。第二个字符使用 2 个代码点。
  • @rdas 您确定为重复的问题实际上与此处描述的问题相反。
  • 重复的目标并不能很好地解释这种特殊情况,IMO,但请查看comparing strings 上的 Unicode How-to 部分,了解 unicode 如何构成重音字符两个单独的字符。
  • @CommandMe 我在 MacOS 上的 Python 3.7 和 3.8 中都测试了len('lòng'),结果是 4,而不是 5。问题无法重现。

标签: python string unicode


【解决方案1】:

这里的问题是,在 unicode 中,某些字符可能由其他字符的组合组成。在这种情况下,“lòng”包括小写“o”和作为单独字符的重音符号。

>>> import unicodedata as ud
>>> w ='lòng'
>>> for c in w:
...     print(ud.name(c))
... 
LATIN SMALL LETTER L
LATIN SMALL LETTER O
COMBINING GRAVE ACCENT
LATIN SMALL LETTER N
LATIN SMALL LETTER G

这是一个分解 unicode 字符串,因为带重音的'o' 分解成两个字符。 unicodedata 模块提供了normalize 函数在分解和组合形式之间进行转换:

>>> for c in ud.normalize('NFC', w):
...     print(ud.name(c))
... 
LATIN SMALL LETTER L
LATIN SMALL LETTER O WITH GRAVE
LATIN SMALL LETTER N
LATIN SMALL LETTER G

如果您想知道字符串是否被规范化为特定形式,但又不想实际规范化它,并且正在使用 Python 3.8+,则可以使用更高效的unicodedata.is_normalized 函数(credit致用户敏锐度):

>>> ud.is_normalized('NFC', w)
False
>>> ud.is_normalized('NFD', w)
True

Python 文档中的Unicode HOWTO 包含一个关于comparing strings 的部分,其中对此进行了更详细的讨论。

【讨论】:

    【解决方案2】:

    Unicode 在编码字符时提供了很大的灵活性。在这种情况下, 实际上由 2 个 Unicode 代码点组成,一个用于基本字符 o,一个用于重音符号。 Unicode 也有一个同时代表两者的字符,它不关心你使用哪个。 Unicode 允许对字符进行编码具有很大的灵活性。 Python 包含一个包unicodedata,它可以提供一致的表示。

    >>> import unicodedata
    >>> w ='lòng'
    >>> len(w)
    5
    >>> len(unicodedata.normalize('NFC', w))
    4
    

    【讨论】:

    【解决方案3】:

    问题在于len 函数和in 运算符损坏 w.r.t。统一码。

    到目前为止,有两个答案声称标准化是解决方案。不幸的是,一般情况下并非如此:

    >>> w = 'Ꙝ̛͋ᄀᄀᄀ각ᆨᆨ?❤️??'
    >>> len(w)
    19
    >>> import unicodedata
    >>> len(unicodedata.normalize('NFC', w))
    19
    >>> # 19 is still wrong
    

    要正确处理这个任务,你需要对字素进行操作:

    >>> from grapheme import graphemes
    >>> w = 'Ꙝ̛͋ᄀᄀᄀ각ᆨᆨ?❤️??'
    >>> len(list(graphemes(w)))
    3
    >>> # 3 is correct
    >>> for g in graphemes(w):
    ...     print(g)
    Ꙝ̛͋
    ᄀᄀᄀ각ᆨᆨ
    ?❤️??
    

    也适用于您的 w = 'lòng' 输入,无需任何规范化即可正确分段为 4。

    【讨论】:

    • 是的,在 3.8 中仍然存在问题。你可以自己测试一下。他们只是更新了一些数据文件,当然这不会改变len 的工作方式。
    • 你可以在你的答案中链接到你正在谈论的第三方包。
    • 我在 MacOS 上的 Python 3.7 和 3.8 中都测试了len('lòng'),它是 4,而不是 5。至于你的字符串 Ꙝ̛͋ᄀᄀᄀ각ᆨᆨ?❤️??,它的长度是 14,而不是 19。
    • 您的lòng 与此线程中所有其他参与者正在使用的lòng 不同。 ––– Stackexchange 软件破坏了我的示例输入。这里又是转义符号,所以我可以绕过这个错误: \N{U+0A65C}\N{U+0031B}\N{U+0034B}\N{U+00356}\N{U+00489}\ N{U+01100}\N{U+01100}\N{U+01100}\N{U+0AC01}\N{U+011A8}\N{U+011A8}\N{U+1F469}\N {U+0200D}\N{U+02764}\N{U+0FE0F}\N{U+0200D}\N{U+1F48B}\N{U+0200D}\N{U+1F469}
    • 我只能使用 Python 2.7 复制原始问题,这是人们不应该再使用的 Python 版本。
    猜你喜欢
    • 1970-01-01
    • 2011-05-13
    • 1970-01-01
    • 2014-03-29
    • 2016-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多