使用 UNICODE_CHARACTER_CLASS 标志时不同的 Java 正则表达式匹配行为

Different Java Regex matching behavior when using UNICODE_CHARACTER_CLASS flag

我正在针对不同的标点符号测试 Pattern.UNICODE_CHARACTER_CLASS 标志的行为,并注意到重音字符 (U+0060) ` 的匹配会根据是否 Pattern.UNICODE_CHARACTER_CLASS 被使用。

例如,看下面的代码:


public class GraceAccentTest {
    public static void main(String args[]) {
       Pattern p = Pattern.compile("\p{Punct}");
       Matcher m = p.matcher("`");
       System.out.println(m.matches()); // returns true
       
       Pattern p1 = Pattern.compile("\p{Punct}", Pattern.UNICODE_CHARACTER_CLASS);
       Matcher m1 = p1.matcher("`");
       System.out.println(m1.matches()); // returns false 
    }
}

当我不使用 Pattern.UNICODE_CHARACTER_CLASS 标记重音字符与 \p{Punct} 字符匹配时 class 但是当我使用该标记时它不匹配。有人可以解释一下这样做的原因吗?

正在阅读 UNICODE_CHARACTER_CLASS

的文档

When this flag is specified then the (US-ASCII only) Predefined character classes and POSIX character classes are in conformance with Unicode Technical Standard #18: Unicode Regular Expression Annex C: Compatibility Properties.

所以这就是说只使用 US-ASCII。因此,如果您检查字符 标点符号 的 table,您将检查是否有很多缺失的字符。

表格:

https://www.fileformat.info/info/unicode/category/Po/list.htm

https://www.gaijin.at/en/infos/unicode-character-table-punctuation

当你使用Pattern p = Pattern.compile("\p{Punct}");时,那么\p{Punct}指的是下面的32个字符:

!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

参考:the Pattern class.

这32个字符对应ASCII字符集字符0x210x7e,不包括字母和数字。它们也恰好代表了我的标准 U.S 上的所有 non-letter 和 non-digit 符号。键盘(当然,您的键盘可能不同)。

重音符(也称为反引号)在该列表和我的键盘上。

这是一个“预定义字符 class”的简单示例 - 并解释了为什么您的 m.matches() returns true.


当您添加 Pattern.UNICODE_CHARACTER_CLASS 标志时,事情会变得更加复杂。

正如该标志的 documentation 所解释的,它:

Enables the Unicode version of Predefined character classes and POSIX character classes.

和:

When this flag is specified then the (US-ASCII only) Predefined character classes and POSIX character classes are in conformance with Unicode Technical Standard #18: Unicode Regular Expressions Annex C: Compatibility Properties.

查看上面提到的 Annex C,我们发现 table 显示“为兼容性推荐的分配 属性 名称”。

对于我们的 属性 名称 (punct),标准建议是使用这样定义的字符:

\p{gc=Punctuation}

这里,“gc”代表“一般类别”。 Unicode 字符被分配了一个 "general category" 值。在这种情况下,即 Punctuation - 也缩写为 P 并进一步分解为各种 sub-categories,例如 Pc 用于连接器,Pd 用于破折号,以及很快。 “其他标点符号”还有一个catch-all Po

坟墓字符分配给 Unicode 中的 Symbol 通用类别 - 以及 Modifier 子类别。您可以看到分配给 Sk here.

将它与诸如 ASCII 感叹号之类的字符(也是我们原始 \p{Punct} 列表的一部分,如上所示)进行对比。对于那个we can see,一般类别分配是Po

这解释了为什么当我们将 Pattern.UNICODE_CHARACTER_CLASS 标志添加到我们的原始模式时坟墓不再匹配。

它被分配到与我们在正则表达式中使用的标点符号类别不同的​​一般类别。


下一个明显的问题是 为什么坟墓字符没有包含在 Unicode Po 通用类别中? 为什么它在 Sk 中?

对此我没有很好的答案 - 我敢肯定有“历史原因”。但是,值得注意的是,Sk 类别包括诸如尖音符、变音符、分音符等字符,以及(如前所述)我们的重音符。

所有这些都是变音符号 - 通常与基本字母结合使用以改变发音。所以也许这就是根本原因。

坟墓有点奇怪,也许是因为它除了用作变音符号外还有历史用途。

首先问坟墓如何成为原始 ASCII 字符集的一部分可能更相关。 backtick.

的维基百科页面提供了一些关于此的背景信息