正则表达式 \p{Cs} 不匹配 Java 16 中的符号
Regex \p{Cs} not matching symbol in Java 16
有谁知道为什么正则表达式 \p{Cs}
与 Java 16 中的符号 </code> 不匹配?
曾经在Java11.</p>中匹配过
<p>Java11</p>
<pre><code>jshell
| Welcome to JShell -- Version 11.0.7
| For an introduction type: /help intro
jshell> import java.util.regex.*
jshell> var text = new StringBuilder().appendCodePoint(55622).appendCodePoint(56380)
text ==>
jshell> Pattern.compile("\p{Cs}").matcher(text).find()
==> true
Java 16
INFO: Created user preferences directory.
| Welcome to JShell -- Version 16.0.1
| For an introduction type: /help intro
jshell> import java.util.regex.*
jshell> var text = new StringBuilder().appendCodePoint(55622).appendCodePoint(56380)
text ==>
jshell> Pattern.compile("\p{Cs}").matcher(text).find()
==> false
首先,您的“符号 </code>”的代码点为 399420,该代码点尚未由 Unicode 标准分配(尚未),因此如果您在这里看到有用的东西,则它不是-您系统的标准行为。</p>
<p>您构造字符串的方式在语义上是不正确的,但恰好创建了预期的字符串。由于历史原因,Java 的 API 以 UTF-16 表示为中心。</p>
<p>当您使用两个代理字符定义符号时,即</p>
<pre><code>var text = "\uD946\uDC3C";
System.out.println(text.codePointAt(0));
你会得到
399420
另一方面,当您使用
var text = new StringBuilder().appendCodePoint(399420);
text.chars().forEach(c -> System.out.printf("\u%04X", c));
System.out.println();
你会得到
\uD946\uDC3C
换句话说,两个代理UTF-16char
单元\uD946
、\uDC3C
的序列等同于单个代码点399420
。从概念上讲,字符串由单个代码点组成,换句话说,
System.out.println(text.codePointCount(0, text.length()) + " codepoint(s)");
System.out.println(text.codePointAt(0));
System.out.println("type " + Character.getType(text.codePointAt(0)));
将打印
1 codepoint(s)
399420
type 0
无论哪种情况。类型 0
表示此代码点未分配。
您正在使用 appendCodePoint
将两个 UTF-16 单元附加到 StringBuilder
,但是由于此方法以与 UTF-16 单元相同的方式处理 BMP 的代码点,它也恰好构造相同的字符串。
由于代码点的类别是“未分配”,它不应该是“代理项”,所以 \p{Cs}
永远不会在这里找到匹配项。当处理一个有效的 Unicode 字符串时,你不应该遇到这个类别,因为它只能匹配不能被解释为 BMP.
之外的代码点的悬挂代理字符。
但是有一个错误 JDK-8247546, Pattern matching does not skip correctly over supplementary characters。在 Java 16 之前,正则表达式引擎确实正确处理了位置 0 处的代码点,但只前进了一个 char
位置,因此它在查看 char
位置时发现了一个悬空的代理字符 1
一个人。
我们可以使用
来验证它
var m = Pattern.compile("\p{Cs}").matcher(text);
if(m.find()) {
System.out.println("found a match at " + m.start());
}
在 JDK 16 之前打印“在 1 处找到匹配项”,这是错误的,因为当在 char
位置有一个代码点时应该跳过位置 1
0
和 1
.
此错误已在 JDK 16 中修复。因此,现在,该字符串被视为“未分配”类别的单个代码点。当然,这个类别将来可能会再次发生变化。但它永远不应该是“代理人”。
有谁知道为什么正则表达式 \p{Cs}
与 Java 16 中的符号 </code> 不匹配?
曾经在Java11.</p>中匹配过
<p>Java11</p>
<pre><code>jshell
| Welcome to JShell -- Version 11.0.7
| For an introduction type: /help intro
jshell> import java.util.regex.*
jshell> var text = new StringBuilder().appendCodePoint(55622).appendCodePoint(56380)
text ==>
jshell> Pattern.compile("\p{Cs}").matcher(text).find()
==> true
Java 16
INFO: Created user preferences directory.
| Welcome to JShell -- Version 16.0.1
| For an introduction type: /help intro
jshell> import java.util.regex.*
jshell> var text = new StringBuilder().appendCodePoint(55622).appendCodePoint(56380)
text ==>
jshell> Pattern.compile("\p{Cs}").matcher(text).find()
==> false
首先,您的“符号 </code>”的代码点为 399420,该代码点尚未由 Unicode 标准分配(尚未),因此如果您在这里看到有用的东西,则它不是-您系统的标准行为。</p>
<p>您构造字符串的方式在语义上是不正确的,但恰好创建了预期的字符串。由于历史原因,Java 的 API 以 UTF-16 表示为中心。</p>
<p>当您使用两个代理字符定义符号时,即</p>
<pre><code>var text = "\uD946\uDC3C";
System.out.println(text.codePointAt(0));
你会得到
399420
另一方面,当您使用
var text = new StringBuilder().appendCodePoint(399420);
text.chars().forEach(c -> System.out.printf("\u%04X", c));
System.out.println();
你会得到
\uD946\uDC3C
换句话说,两个代理UTF-16char
单元\uD946
、\uDC3C
的序列等同于单个代码点399420
。从概念上讲,字符串由单个代码点组成,换句话说,
System.out.println(text.codePointCount(0, text.length()) + " codepoint(s)");
System.out.println(text.codePointAt(0));
System.out.println("type " + Character.getType(text.codePointAt(0)));
将打印
1 codepoint(s)
399420
type 0
无论哪种情况。类型 0
表示此代码点未分配。
您正在使用 appendCodePoint
将两个 UTF-16 单元附加到 StringBuilder
,但是由于此方法以与 UTF-16 单元相同的方式处理 BMP 的代码点,它也恰好构造相同的字符串。
由于代码点的类别是“未分配”,它不应该是“代理项”,所以 \p{Cs}
永远不会在这里找到匹配项。当处理一个有效的 Unicode 字符串时,你不应该遇到这个类别,因为它只能匹配不能被解释为 BMP.
但是有一个错误 JDK-8247546, Pattern matching does not skip correctly over supplementary characters。在 Java 16 之前,正则表达式引擎确实正确处理了位置 0 处的代码点,但只前进了一个 char
位置,因此它在查看 char
位置时发现了一个悬空的代理字符 1
一个人。
我们可以使用
来验证它var m = Pattern.compile("\p{Cs}").matcher(text);
if(m.find()) {
System.out.println("found a match at " + m.start());
}
在 JDK 16 之前打印“在 1 处找到匹配项”,这是错误的,因为当在 char
位置有一个代码点时应该跳过位置 1
0
和 1
.
此错误已在 JDK 16 中修复。因此,现在,该字符串被视为“未分配”类别的单个代码点。当然,这个类别将来可能会再次发生变化。但它永远不应该是“代理人”。