【问题标题】:Java regex doesnt match outside of ascii range, behaves different than python regexJava 正则表达式在 ascii 范围之外不匹配,行为不同于 python 正则表达式
【发布时间】:2018-08-30 17:38:05
【问题描述】:

我想像 sklearn 的 CountVectorizer 一样从文档中过滤字符串。它使用以下正则表达式:(?u)\b\w\w+\b。 此 java 代码的行为方式应该相同:

Pattern regex = Pattern.compile("(?u)\\b\\w\\w+\\b");
Matcher matcher = regex.matcher("this is the document.!? äöa m²");

while(matcher.find()) {
    String match = matcher.group();
    System.out.println(match);
}

但这不会像在 python 中那样产生所需的输出:

this
is
the
document
äöa
m²

它改为输出:

this
is
the
document

我可以做些什么来包含非 ascii 字符,就像 python RegEx 所做的那样?

【问题讨论】:

  • 试试"(?U)\\b\\w\\w+\\b" 或只是"(?U)\\w{2,}"
  • @WiktorStribiżew 对äöa 有效,但对 无效
  • 谢谢!这适用于德语字母,但仍然不包括平方符号 (²),知道如何解决这个问题吗?
  • @LanceToth 我正在使用 Java,而不是 JavaScript

标签: java regex scikit-learn pattern-matching countvectorizer


【解决方案1】:

还有一步:您需要指定\w 也包含Unicode 字符。 Pattern.UNICODE_CHARACTER_CLASS救援:

    Pattern regex = Pattern.compile("(?u)\\b\\w\\w+\\b", Pattern.UNICODE_CHARACTER_CLASS);
                                                   // ^^^^^^^^^^
    Matcher matcher = regex.matcher("this is the document.!? äöa m²");

    while(matcher.find()) {
        String match = matcher.group();
        System.out.println(match);
    }

【讨论】:

    【解决方案2】:

    根据 Wiktor 在 cmets 中的建议,您可以使用 (?U) 打开标志 UNICODE_CHARACTER_CLASS。虽然这确实允许匹配äöa,但这仍然不匹配。这是因为 UNICODE_CHARACTER_CLASS\w 无法将 ² 识别为有效的字母数字字符。作为\w 的替代品,您可以使用[\pN\pL_]。这匹配 Unicode 数字 \pN 和 Unicode 字母 \pL(加上 _)。 \pN Unicode 字符类包括\pNo 字符类,其中包括Latin 1 Supplement - Latin-1 标点符号和符号 字符类(它包括²³¹)。或者,您可以将\pNo Unicode 字符类添加到具有\w 的字符类中。这意味着以下正则表达式正确匹配您的字符串:

    [\pN\pL_]{2,}         # Matches any Unicode number or letter, and underscore
    (?U)[\w\pNo]{2,}      # Uses UNICODE_CHARACTER_CLASS so that \w matches Unicode.
                          # Adds \pNo to additionally match ²³¹
    

    那么为什么 \w 在 Java 中不匹配 ² 而在 Python 中匹配呢?


    Java的解释

    查看OpenJDK 8-b132's Pattern implementation,我们得到以下信息(我删除了与回答问题无关的信息):

    Unicode 支持

    以下预定义字符类POSIX字符 类 符合附录 C 的建议: Unicode 正则表达式的兼容性属性,当 指定了UNICODE_CHARACTER_CLASS 标志。

    \w一字字:[\p{Alpha}\p{gc=Mn}\p{gc=Me}\p{gc=Mc}\p{Digit}\p{gc=Pc}\p{IsJoin_Control}]

    太棒了!现在,当使用(?U) 标志时,我们有一个\w定义。将这些 Unicode 字符类插入this amazing tool 将准确地告诉您这些 Unicode 字符类中的每一个匹配什么。在不让这篇文章超长的情况下,我会继续告诉你以下课程都不匹配²

    • \p{Alpha}
    • \p{gc=Mn}
    • \p{gc=Me}
    • \p{gc=Mc}
    • \p{Digit}
    • \p{gc=Pc}
    • \p{IsJoin_Control}

    Python的解释

    那么当u 标志与\w 一起使用时,为什么Python 匹配²³¹?这个很难找到,但我深入研究了Python's source code (I used Python 3.6.5rc1 - 2018-03-13)。在消除了很多关于如何调用它的绒毛之后,基本上会发生以下情况:

    • \w 定义为CATEGORY_UNI_WORD,然后以SRE_ 为前缀。 SRE_CATEGORY_UNI_WORD 致电SRE_UNI_IS_WORD(ch)
    • SRE_UNI_IS_WORD 定义为 (SRE_UNI_IS_ALNUM(ch) || (ch) == '_')
    • SRE_UNI_IS_ALNUM 调用Py_UNICODE_ISALNUM,而Py_UNICODE_ISALNUM 又被定义为(Py_UNICODE_ISALPHA(ch) || Py_UNICODE_ISDECIMAL(ch) || Py_UNICODE_ISDIGIT(ch) || Py_UNICODE_ISNUMERIC(ch))
    • 这里重要的是Py_UNICODE_ISDECIMAL(ch),定义为Py_UNICODE_ISDECIMAL(ch) _PyUnicode_IsDecimalDigit(ch)

    现在,我们来看看_PyUnicode_IsDecimalDigit(ch)的方法:

    int _PyUnicode_IsDecimalDigit(Py_UCS4 ch)
    {
        if (_PyUnicode_ToDecimalDigit(ch) < 0)
            return 0;
        return 1;
    }
    

    如我们所见,如果_PyUnicode_ToDecimalDigit(ch) &lt; 0,此方法返回1。那么_PyUnicode_ToDecimalDigit 长什么样子呢?

    int _PyUnicode_ToDecimalDigit(Py_UCS4 ch)
    {
        const _PyUnicode_TypeRecord *ctype = gettyperecord(ch);
    
        return (ctype->flags & DECIMAL_MASK) ? ctype->decimal : -1;
    }
    

    太好了,所以基本上,如果字符的 UTF-32 编码字节具有 DECIMAL_MASK 标志,这将评估为 true,并且将返回大于或等于 0 的值。

    ² 的 UTF-32 编码字节值是 0x000000b2,我们的标志 DECIMAL_MASK0x020x000000b2 &amp; 0x02 的计算结果为 true,因此 ² 在 python 中被视为有效的 Unicode 字母数字字符,因此带有 u 标志的 \w² 匹配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-14
      • 1970-01-01
      • 1970-01-01
      • 2015-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多