使用Unicode字符串的正则表达式字边界的意外行为

时间:2015-12-25 17:38:07

标签: regex string python-2.7 unicode

有人可以解释一下正则表达式的这种行为:

当我用一些其他Unicode字符替换Unicode字符串的最后两个字符时,它在字符串末尾的行边界($)下工作正常但如果我指定{{1}则会产生意外结果方括号$

字边界[$]也会产生意想不到的结果,令人惊讶\b\B匹配的内容相匹配。

\b

1 个答案:

答案 0 :(得分:1)

  1. re.sub的签名是:

    sub(pattern, repl, string, count=0, flags=0)
    

    re.U标志正传递给count,因此re.U标志不执行任何操作。确保使用关键字参数,如:

    re.sub(ur'\u06cc\u0670\b', u'\u0627', line, flags=re.U)
    #                                           ^~~~~~ 
    
  2. […]定义了一个字符类,$在括号内并不特殊。因此[$]只会匹配一个字面的美元符号。

  3. \b匹配单词(“\ w”)和非单词(“\ W”,或字符串的开头/结尾)之间的边界,\B匹配任何地方那不是\b。现在,\u0670是Unicode中的非单词:

    >>> re.findall(ur'\w', line, flags=re.U)
    [u'\u0627', u'\u062f', u'\u0646', u'\u06cc']
    >>> re.findall(ur'\W', line, flags=re.U)
    [u'\u0670']
    

    这意味着\u0670之后的字符串结尾不是字边界,因为\u0670不是单词。因此\b无法与之匹配,这意味着\B将与之匹配。

    Unicode中\w的含义是“[0-9_]加上Unicode字符属性数据库中的classified as alphanumeric”。

    像U + 06CC(阿拉伯字母波斯语)这样的字符被归类为字母,其他(Lo)所以它是一个单词,但是U + 0670(阿拉伯字母Superscript Alef)被归类为< strong> Mark,Nonspacing(Mn)因此

  4. (您可以在https://docs.python.org/2/library/re.html

    中查看Python正则表达式语法的详细信息

    对于以下评论,您可以使用negative look-ahead代替小组:

    re.sub(ur'(?:[\u06cc\u06d2]\u0670|\u0670[\u06cc\u06d2])(?!\w)', u'\u0627', line, flags=re.U)
    

    下面,

    • [\u06cc\u06d2]\u0670|\u0670[\u06cc\u06d2]与您的\u06cc\u0670|\u06d2\u0670|\u0670\u06cc|\u0670\u06d2相同,但类似的案例组合在一起
    • (?:…)定义non-capturing group,以便您可以从更改中提取所需的“\ b”
    • (?!\w)表示只有在下一个字符不是单词时才匹配。

    结果如下:

    >>> re.sub(u'(?:[\u06cc\u06d2]\u0670|\u0670[\u06cc\u06d2])(?!\w)', u'\u0627', line, flags=re.U)
    u'\u0627\u062f\u0646\u0627'
    >>> re.sub(u'(?:[\u06cc\u06d2]\u0670|\u0670[\u06cc\u06d2])(?!\w)', u'\u0627', line + u'\u0646', flags=re.U)
    u'\u0627\u062f\u0646\u06cc\u0670\u0646'
    >>> re.sub(u'(?:[\u06cc\u06d2]\u0670|\u0670[\u06cc\u06d2])(?!\w)', u'\u0627', line + u'\u061f', flags=re.U)
    u'\u0627\u062f\u0646\u0627\u061f'