java - 为什么代理 java 正则表达式会找到连字符 -

标签 java regex

我试图找出为什么 JAVA ([\ud800-\udbff\udc00-\udfff]) 中的这个正则表达式用于 replaceAll(regexp,"")还删除连字符减号以及代理字符。

这个的 Unicode 是 \u002d 所以它似乎不在任何这些范围内。

我可以很容易地删除此行为,添加 &&[^\u002d] 导致 ([\ud800-\udbff\udc00-\udfff&&[^\u002d]])

但是,由于我不知道为什么这个 \u002d 被删除,我认为可能有更多未被注意的字符被删除。

例子:

String text = "A\u002dB";
System.out.println(text);
String regex = "([\ud800-\udbff\udc00-\udfff])";
System.out.println(text.replaceAll(regex, "X"));

打印:

A-B
AXB

最佳答案

概述和假设

在星体平面(代码点 U+10000 到 U+10FFFF)中匹配字符一直是 Java 正则表达式中记录不足的功能。

本回答主要是Oracle对Java 6及以上版本的实现(引用实现,OpenJDK中也有使用)。

如果您碰巧使用 GNU Classpath 或 Android,请自行测试代码,因为它们使用自己的实现。

幕后花絮

假设您在 Oracle 的实现上运行您的正则表达式,您的正则表达式

"([\ud800-\udbff\udc00-\udfff])"

这样编译:

StartS. Start unanchored match (minLength=1)
java.util.regex.Pattern$GroupHead
Pattern.union. A ∪ B:
  Pattern.union. A ∪ B:
    Pattern.rangeFor. U+D800 <= codePoint <= U+10FC00.
    BitClass. Match any of these 1 character(s):
      [U+002D]
  SingleS. Match code point: U+DFFF LOW SURROGATES DFFF
java.util.regex.Pattern$GroupTail
java.util.regex.Pattern$LastNode
Node. Accept match

字符类被解析为\ud800-\udbff\udc00,-,\udfff。由于\udbff\udc00构成了一个有效的代理对,它代表了代码点U+10FC00。

错误的解决方案

没有意义写:

"[\ud800-\udbff][\udc00-\udfff]"

由于 Oracle 的实现是按代码点进行匹配的,并且有效的代理项对将在匹配之前转换为代码点,因此上面的正则表达式无法匹配任何内容,因为它正在搜索 2 个连续的单独的可以形成有效对的代理项。

解决方案

如果你想匹配并删除星体层中 U+FFFF 以上的所有代码点(由有效的代理对形成),加上孤独的代理(不能形成有效的代理对),你应该写:

input.replaceAll("[\ud800\udc00-\udbff\udfff\ud800-\udfff]", "");

此解决方案已经过测试,可以在 Java 6 和 7(Oracle 实现)中工作。

上面的正则表达式编译为:

StartS. Start unanchored match (minLength=1)
Pattern.union. A ∪ B:
  Pattern.rangeFor. U+10000 <= codePoint <= U+10FFFF.
  Pattern.rangeFor. U+D800 <= codePoint <= U+DFFF.
java.util.regex.Pattern$LastNode
Node. Accept match

请注意,我使用字符串文字 Unicode 转义序列指定字符,而不是正则表达式语法中的转义序列。

// Only works in Java 7
input.replaceAll("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]", "")

当用正则表达式语法指定时,Java 6 不识别代理项对,因此正则表达式将 \\ud800 识别为一个字符并尝试编译范围 \\udc00-\\udbff 它失败的地方。我们很幸运,它为此输入抛出了一个异常;否则,错误将不会被发现。 Java 7 正确解析此正则表达式并编译为与上述相同的结构。


从 Java 7 及更高版本开始,语法 \x{h..h} 已被添加以支持指定超出 BMP(基本多语言平面)的字符,这是指定字符的推荐方法星体层。

input.replaceAll("[\\x{10000}-\\x{10ffff}\ud800-\udfff]", "");

这个正则表达式也编译成与上面相同的结构。

关于java - 为什么代理 java 正则表达式会找到连字符 -,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27820971/

相关文章:

regex - 如何在 bash 中匹配连字符

java - 添加行@throws java.io.IOException 有什么用?

Java 默认方法比相同的代码慢,但在抽象类中

javascript - 将 Golang 正则表达式转换为 JS 正则表达式

php - 正则表达式检查路径是否只向下

javascript - 如何使用 Javascript .replace() 和正则表达式模式限制字符串长度?

带有空白字符的正则表达式

java - 如何将子文件夹请求转换为参数

java - 使用计数器的 Douglas Peucker 算法

java - 当 cookie 包含变音字符时 Tomcat 7 异常