8

有问题的字符串有一个补充 unicode 字符“\ud84c\udfb4”。根据 javadoc,正则表达式匹配应该在代码点级别而不是字符级别进行。但是,下面的拆分代码将低代理 (\udfb4) 视为非单词字符并对其进行拆分。

我错过了什么吗?完成非单词字符拆分的其他替代方法是什么?(Java 版本“1.7.0_07”)

提前致谢。

Pattern non_word_regex = Pattern.compile("[\\W]", Pattern.UNICODE_CHARACTER_CLASS);
String a = "\u529f\u80fd\u0020\u7d76\ud84c\udfb4\u986f\u793a\u5ee3\u544a";
String b ="功能 絶顯示廣告";
System.out.print("original "+a+"\norginal hex ");
for(char c : a.toCharArray()){
    System.out.print(Integer.toHexString((int)c));
    System.out.print(' ');
}
System.out.println();

String[] tokens = non_word_regex.split(a);

for(int i =0; i< tokens.length; i++){
   String token = tokens[i];
   System.out.print(i+" ");
   for(char c : token.toCharArray()){
       System.out.print(Integer.toHexString((int)c));
       System.out.print(' ');
   }
   System.out.println();
}

输出:
original 功能绝显广告<br> original hex 529f 80fd 20 7d76 d84c dfb4 986f 793a 5ee3 544a
0 529f 80fd
1 7d76 d84c
2 986f 793a 5ee3 544a

4

1 回答 1

9

这看起来就像正则表达式引擎中的一个错误。如果您使用该\w表达式,则一切都正确匹配,仍然是由两个字符组成的单个代码点。这可以通过运行以下代码轻松验证:

Pattern pattern = Pattern.compile("(?U)[\\w]");
String str = "功能 絶顯示廣告";

Matcher matcher = pattern.matcher(str);
while (matcher.find()) {
    System.out.println(matcher.toMatchResult().group());
}

我刚刚做了一个彻底的调查,所以我可以告诉你问题出在哪里。如果您查看java.util.regex.Pattern 中compile()的方法(从第 1625 行开始),您将看到扫描正则表达式以查找补充字符并决定是否支持它们进行扫描的代码。

这种方法的问题在于,代码没有考虑到即使正则表达式没有补充字符,它也可能仍然想要匹配它们,例如在您的情况下会发生这种情况。

解决方案是设计一些包含补充字符的正则表达式,但它们不会影响匹配过程。我建议你使用这样无辜的东西:

Pattern nonWordRegex = Pattern.compile("(?U)(?!\uDB80\uDC00)[\\W]");

这部分(?!\uDB80\uDC00)起到了作用。对于补充字符的私有范围中的字符,这是一个否定的前瞻,这意味着您很可能不会在文本中找到它。瞧:正则表达式引擎认为模式中有补充字符,并打开它们的支持!

于 2013-12-10T19:53:42.190 回答