Поддержка Java для символов Unicode, отличных от BMP (т.е. кодовых точек › 0xFFFF) в их библиотеке регулярных выражений?

В настоящее время я использую Java 6 (у меня нет возможности перейти на Java 7) и пытаюсь использовать пакет java.util.regex для сопоставления с образцом строк, содержащих символы Unicode.

Я знаю, что java.lang.String поддерживает дополнительные символы (т. е. символы с кодовыми точками > 0xFFFF) (начиная с Java 5), ​​но я не вижу простого способа сопоставления шаблонов с этими символами. java.util.regex.Pattern по-прежнему позволяет представлять шестнадцатеричные числа только с использованием 4 цифр (например, ￿)

Кто-нибудь знает, не хватает ли мне здесь API?


person Jin Kim    schedule 23.03.2011    source источник
comment
Том Кристиансен (известный Perl) ответил на несколько вопросов о регулярных выражениях Unicode и Java здесь, на SO: stackoverflow.com/users/471272/tchrist< /а>   -  person ninjalj    schedule 24.03.2011


Ответы (2)


Я никогда не выполнял сопоставление шаблонов с дополнительными символами, но я думаю, что это так же просто, как закодировать их (в шаблонах и строках) как два 16-битных числа (суррогатная пара UTF-16) \unnnn\ummmm . java.util.regex должен быть достаточно умен, чтобы интерпретировать эти два числа (символы Java) как один символ в шаблонах и строках (хотя Java по-прежнему будет рассматривать их как два символа, как элементы строки).

Две ссылки:

Кодировка Java Unicode

http://java.sun.com/developer/technicalArticles/Intl/Supplementary/< /а>

Из последней ссылки (относится к Java 5):

Пакет java.util.regex был обновлен таким образом, что строки шаблонов и целевые строки могут содержать дополнительные символы, которые будут обрабатываться как целые единицы.

Также обратите внимание, что если вы используете UTF8 в качестве кодировки (для ваших исходных файлов), вы также можете написать их напрямую (см. раздел «Представление дополнительных символов в исходных файлах» в последней ссылке).

Например:

    String pat1 = ".*\uD840\uDC00{2}.*";
    String s1  = "HI \uD840\uDC00\uD840\uDC00 BYE";
    System.out.println(s1.matches(pat1) + " len=" + s1.length());

    String pat2 = ".*\u0040\u0041{2}.*";
    String s2 = "HI \u0040\u0041\u0040\u0041 BYE";
    System.out.println(s2.matches(pat2) + " len=" + s2.length());

Это, скомпилированное с Java 6, печатает

true len=11
false len=11

что согласуется с вышесказанным. В первом случае у нас есть одна кодовая точка, представленная в виде пары суррогатных символов Java (два 16-битных символа, один дополнительный символ Unicode), и к паре применяется квантификатор {2} (= кодовая точка). Во втором у нас есть два разных символа BMP, квантификатор применяется к последнему — следовательно, совпадений нет.

Обратите внимание, однако, что длина строки одинакова (поскольку Java измеряет длину строки, считая символы Java, а не кодовые точки Unicode).

person leonbloy    schedule 23.03.2011
comment
Я надеялся, что смогу закодировать не-BMP Unicode непосредственно в регулярном выражении (аналогично \Unnnnnn), но я думаю, что UTF-8 - следующая лучшая вещь. Спасибо. - person Jin Kim; 24.03.2011
comment
@Jin: это UTF-16, а не UTF-8. И UTF-16 — это единственный способ, которым Java может внутренне представлять эти символы в String. - person Joachim Sauer; 24.03.2011
comment
Чтобы прояснить приведенные выше комментарии: вы можете либо ввести кодовую точку как \unnnnn\ummmmm в качестве суррогата UTF16, ИЛИ напрямую ввести символ в кодировке, которую вы используете для исходного кода (обычно UTF-8) (но не в форма \u...!). Второй вариант может быть удобным, но также и проблематичным, особенно если вам нужно поделиться своим исходным кодом. - person leonbloy; 25.03.2011
comment
@Jim: Давай JDK7, ты можешь использовать \x{HHHHH}. Это единственный способ заставить их работать, например, в классах персонажей. - person tchrist; 16.04.2011
comment
@Leonblow: На самом деле это не работает в классах персонажей. UTF-16 вас кусает. Вам придется подождать JDK7. Между тем, UTF-8 — ваш лучший выбор. - person tchrist; 16.04.2011

Самое простое решение — использовать кодировку UTF-8 для исходного кода. Затем просто вставьте символы напрямую. Вы никогда не должны указывать отдельные единицы кода в любой программе.

Однако проблема с символьными классами все еще существует, потому что неуклюжая внутренняя кодировка UTF-16 в Java искажает их. Вы не можете использовать полный Unicode до JDK7, где даже тогда вам придется указывать логические кодовые точки, используя косвенную нотацию \x{HHHHH}. Вы по-прежнему не сможете использовать литеральную кодовую точку в charclass, но вы можете избежать этого с помощью \x{H..H}.

Несовершенно, но намного лучше, чем было. UTF-16 — это всегда компромисс. Системы, которые используют UTF-8 или UTF-32 внутри, не имеют этих ограничений. Они также никогда не заставляют вас указывать кодовые единицы, которые не идентичны кодовым точкам.

person tchrist    schedule 16.04.2011