Java正则表达式在ascii范围之外不匹配,其行为与python正则表达式不同
我想像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
像python正则表达式那样,如何包含非ascii字符
# 1 楼答案
正如Wiktor在评论中所建议的,您可以使用}Unicode字符类包括{}字符类,其中包括拉丁1增补-拉丁-1标点符号字符类(包括{})。或者,您可以将
(?U)
来打开标志UNICODE_CHARACTER_CLASS
。虽然这允许匹配äöa
,但仍然不匹配m²
。这是因为UNICODE_CHARACTER_CLASS
with\w
不能将²
识别为有效的字母数字字符。作为\w
的替代,您可以使用[\pN\pL_]
。这与Unicode数字\pN
和Unicode字母\pL
(加上_
)匹配。{\pNo
Unicode字符类添加到具有\w
的字符类中。这意味着以下正则表达式与字符串正确匹配:那么为什么
\w
在Java中不匹配²
而在Python中匹配呢Java的解释
查看OpenJDK 8-b132's ^{} implementation ,我们得到以下信息(我删除了与回答问题无关的信息):
太好了!现在,当使用
(?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_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)
李>现在,让我们看看方法{{CD46}}:
正如我们所看到的,这个方法返回
1
如果_PyUnicode_ToDecimalDigit(ch) < 0
。那么_PyUnicode_ToDecimalDigit
是什么样子的呢很好,所以基本上,如果字符的UTF-32编码字节具有
DECIMAL_MASK
标志,那么这将计算为true,并返回大于或等于0
的值²
的UTF-32编码字节值为0x000000b2
,我们的标志DECIMAL_MASK
为0x02
0x000000b2 & 0x02
的计算结果为true,因此²
在python中被视为有效的Unicode字母数字字符,因此\w
与u
标志匹配²
# 2 楼答案
还有一个步骤:您需要指定
\w
也包含unicode字符Pattern.UNICODE_CHARACTER_CLASS
救援: