在 Python 中匹配 Unicode 字边界

Posted

技术标签:

【中文标题】在 Python 中匹配 Unicode 字边界【英文标题】:Matching Unicode word boundaries in Python 【发布时间】:2017-01-01 03:29:57 【问题描述】:

为了匹配 Python 中的 Unicode 字边界 [如 Annex #29 中定义的那样],我一直在使用带有标志 regex.WORD | regex.V1regex 包(regex.UNICODE 应该是默认值,因为该模式是 Unicode字符串)以下列方式:

>>> s="here are some words"
>>> regex.findall(r'\w(?:\B\S)*', s, flags = regex.V1 | regex.WORD)
['here', 'are', 'some', 'words']

在这种相当简单的情况下效果很好。但是,我想知道如果输入字符串包含某些标点符号,预期的行为是什么。在我看来,WB7 表示,例如,x'z 中的撇号不符合作为单词边界的条件,这似乎确实如此:

>>> regex.findall(r'\w(?:\B\S)*', "x'z", flags = regex.V1 | regex.WORD)
["x'z"]

但是,如果有元音,情况就变了:

>>> regex.findall(r'\w(?:\B\S)*', "l'avion", flags = regex.V1 | regex.WORD)
["l'", 'avion']

这表明正则表达式模块实现了注释部分标准中提到的规则WB5a。但是,这条规则也说行为应该与我无法重现的\u2019(右单引号)相同:

>>> regex.findall(r'\w(?:\B\S)*', "l\u2019avion", flags = regex.V1 | regex.WORD)
['l’avion']

此外,即使使用“正常”撇号,连字(或 y)似乎也表现为“非元音”:

>>> regex.findall(r'\w(?:\B\S)*', "l'œil", flags = regex.V1 | regex.WORD)
["l'œil"]
>>> regex.findall(r'\w(?:\B\S)*', "J'y suis", flags = regex.V1 | regex.WORD)
["J'y", 'suis']

这是预期的行为吗? (以上所有示例均使用 regex 2.4.106 和 Python 3.5.2 执行)

【问题讨论】:

【参考方案1】:

1- 右单引号 似乎只是在source file 中被遗漏了:

/* Break between apostrophe and vowels (French, Italian). */
/* WB5a */
if (pos_m1 >= 0 && char_at(state->text, pos_m1) == '\'' &&
  is_unicode_vowel(char_at(state->text, text_pos)))
    return TRUE;

2- Unicode 元音由is_unicode_vowel() 函数确定,该函数转换为该列表:

a, à, á, â, e, è, é, ê, i, ì, í, î, o, ò, ó, ô, u, ù, ú, û

所以 LATIN SMALL LIGATURE OE œ 字符不被视为 unicode 元音:

Py_LOCAL_INLINE(BOOL) is_unicode_vowel(Py_UCS4 ch) 
#if PY_VERSION_HEX >= 0x03030000
    switch (Py_UNICODE_TOLOWER(ch)) 
#else
    switch (Py_UNICODE_TOLOWER((Py_UNICODE)ch)) 
#endif
    case 'a': case 0xE0: case 0xE1: case 0xE2:
    case 'e': case 0xE8: case 0xE9: case 0xEA:
    case 'i': case 0xEC: case 0xED: case 0xEE:
    case 'o': case 0xF2: case 0xF3: case 0xF4:
    case 'u': case 0xF9: case 0xFA: case 0xFB:
        return TRUE;
    default:
        return FALSE;
    

这个错误现在在正则表达式 2016.08.27 在bug report 之后得到修复。 [_regex.c:#1668]

【讨论】:

第 1 点确实看起来像一个错误,如果您还没有报告,我建议您报告它。关于第 2 点很难说。在 unicode.org 上搜索“元音”可以找到多种亚洲语言的匹配结果,但没有找到法语或意大利语。 OP 的示例当然看起来是正确的,但我看不到附件 29 专门针对它们。

以上是关于在 Python 中匹配 Unicode 字边界的主要内容,如果未能解决你的问题,请参考以下文章

python中sys.setdefaultencoding('utf-8')的作用

python中sys.setdefaultencoding('utf-8')的作用

试图在字边界之外匹配零

如何使用 DFA 正则表达式匹配器实现正则表达式断言/环视(即 \b 样式字边界)

Javascript RegExp + 单词边界 + unicode 字符

Oracle REGEXP_LIKE 和字边界