Python unicode正则表达式匹配失败了一些unicode字符-bug或错误?

时间:2012-10-05 12:39:18

标签: python regex unicode

我试图使用Python 2.7.3中的re模块和Unicode编码的Devnagari文本。我在代码的顶部添加了from __future__ import unicode_literals,因此所有字符串文字都应该是unicode对象。

然而,我遇到了Python正则表达式匹配的一些奇怪问题。例如,考虑这个名字:“किशोरी”。这是一个(拼写错误的)名字,用印地语,由我的一个用户输入。任何印地语读者都会认识到这一点。

以下内容将返回匹配项:

re.search("^[\w\s][\w\s]*","किशोरी",re.UNICODE)

但这不是:

re.search("^[\w\s][\w\s]*$","किशोरी",re.UNICODE)

一些探险者发现,此字符串中只有一个字符,字符0915(क)被识别为属于\ w字符类。这是不正确的,因为Unicode字符数据库file on "derived core properties"将此字符串中的其他字符(我没有全部检查过)列为字母字符串 - 事实上它们确实如此。

这只是Python实现中的一个错误吗?我可以通过手动将所有Devnagari字母数字字符定义为字符范围来解决这个问题,但这会很痛苦。或者我做错了什么?

3 个答案:

答案 0 :(得分:7)

这是一个bug in the re module,它已在regex module中修复:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import unicodedata
import re
import regex  # $ pip install regex

word = "किशोरी"


def test(re_):
    assert re_.search("^\\w+$", word, flags=re_.UNICODE)

print([unicodedata.category(cp) for cp in word])
print(" ".join(ch for ch in regex.findall("\\X", word)))
assert all(regex.match("\\w$", c) for c in ["a", "\u093f", "\u0915"])

test(regex)
test(re)  # fails

输出显示"किशोरी"中有6个代码点,但只有3个用户感知的字符(扩展的字形集群)。 在字符内打破字样是错误的。 Unicode Text Segmentation说:

  

词边界,行边界和句子边界不应该   在字形集群中发生:换言之,字形集群   应该是关于确定过程的原子单位   这些其他界限。

这里进一步强调我的

字边界\b被定义为the docs中从\w\W(或相反)的转换:

  

注意,正式地,\ b被定义为\ w和a之间的边界   \ W字符(反之亦然),或\ w与\ n开头/结尾之间   字符串,......

因此,构成单个字符的所有代码点都是\w,或者它们都是\W。 在这种情况下,"किशोरी"^\w{6}$匹配。


来自the docs for \w in Python 2

  

如果设置了UNICODE,则会匹配字符[0-9_] plus   在Unicode字符中被归类为字母数字的任何内容   属性数据库<​​/ em>。

Python 3中的

  

匹配Unicode字符;这个包含了大多数字符   可以是任何语言中的单词的一部分,以及数字和   下划线。

来自regex文档:

  

'word'字符的定义(issue #1693050):

     

为Unicode扩展了“单词”字符的定义。它现在符合Unicode规范   http://www.unicode.org/reports/tr29/。这适用于\ w,\ W,\ b和   \乙

根据unicode.org U+093F (DEVANAGARI VOWEL SIGN I)是alnum和字母,因此即使我们遵循不基于单词边界的定义,regex也可以考虑它\w

答案 1 :(得分:3)

来自角色地图:

  

ि

     

U + 093F DEVANAGARI VOWEL SIGN I

     

一般字符属性

     

在Unicode中:1.1   Unicode类别:标记,间距合并

因此,从技术上讲,这不是一封信,即使\w也不属于re.UNICODE。您可以尝试使用带有Unicode字符属性的regex来包含这些类型的字符。

答案 2 :(得分:2)

我测试了以下内容:

import unicodedata
for c in "किशोरी":
    print unicodedata.category(c)
    print unicodedata.name(c)

在我的案例中显示:

Lo
DEVANAGARI LETTER KA
Mc
DEVANAGARI VOWEL SIGN I
Lo
DEVANAGARI LETTER SHA
Mc
DEVANAGARI VOWEL SIGN O
Lo
DEVANAGARI LETTER RA
Mc
DEVANAGARI VOWEL SIGN II

Unicode的东西很难调试,因为复制和粘贴会弄乱数据,我不知道印地文。但在某些语言中,您可以在unicode中以不同方式编码字符。是否有可能在匹配之前必须以某种方式规范化字符串?对我而言,元音符号与\w不匹配。