Java 7, регулярные выражения и дополнительные символы Юникода

Рассматриваемая строка имеет дополнительный символ 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();
}

Выход:
оригинал 功能 絶顯示廣告
оригинальный гекс 529f 80fd 20 7d76 d84c dfb4 986f 793a 5ee3 544a
0 529f 80fd
1 7d76 d84c
2 986f 793a 5ee3 544a

1 ответ

Решение

Это выглядит просто как ошибка в движке регулярных выражений. Если вы используете \w выражение, все соответствует правильно, остается единой кодовой точкой, состоящей из двух символов. Это легко проверить, выполнив следующий код:

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

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

Я только что провел тщательное расследование, и поэтому могу сказать вам, где проблема. Если вы посмотрите на метод compile() в java.util.regex.Pattern (начинаются со строки 1625) вы увидите код, который сканирует регулярное выражение на наличие дополнительных символов и решает, поддерживать их в сканировании или нет.

Проблема этого подхода заключается в том, что в коде не учитывается тот факт, что даже если регулярное выражение не имеет дополнительных символов, оно все равно может захотеть сопоставить их, как, например, в вашем случае.

Решение состоит в том, чтобы разработать некоторое регулярное выражение, содержащее дополнительные символы, но они не влияют на процесс сопоставления. Я предлагаю вам использовать что-то невинное, как это:

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

Часть (?!\uDB80\uDC00) делает трюк. Это негативный взгляд на персонажа в закрытом диапазоне дополнительных символов, что означает, что, скорее всего, вы не найдете его в тексте. И вуаля: движок регулярных выражений считает, что в шаблоне есть дополнительные символы, и включает их поддержку!

Другие вопросы по тегам