Многие современные реализации регулярных выражений интерпретируют \w
сокращение класса символов как «любую букву, цифру или соединительную пунктуацию» (обычно: подчеркивание). Таким образом, регулярное выражение , как \w+
спички слова , как hello
, élève
, GOÄ_432
или gefräßig
.
К сожалению, в Java нет. В Java \w
ограничено [A-Za-z0-9_]
. Это, среди прочего, затрудняет сопоставление слов, подобных упомянутым выше.
Также кажется, что \b
разделитель слов совпадает там, где он не должен.
Что было бы правильным эквивалентом .NET-подобного, поддерживающего Unicode \w
или \b
Java? Какие еще ярлыки нужно «переписать», чтобы они поддерживали Unicode?
java
regex
unicode
character-properties
Тим Пицкер
источник
источник
Ответы:
Исходный код
Исходный код функций перезаписи, которые я обсуждаю ниже , доступен здесь. .
Обновление в Java 7
Обновленный
Pattern
класс Sun для JDK7 имеет чудесный новый флагUNICODE_CHARACTER_CLASS
, который заставляет все снова работать правильно. Он доступен как встраиваемый(?U)
для внутри шаблона, поэтому вы также можете использовать его сString
оболочками класса. Также были исправлены определения для различных других свойств. Теперь он отслеживает стандарт Unicode в RL1.2 и RL1.2a из UTS # 18: Регулярные выражения Unicode . Это захватывающее и значительное улучшение, и следует поблагодарить команду разработчиков за это важное усилие.Проблемы Unicode в Java Regex
Проблема с Java регулярных выражений является то , что Perl 1.0 charclass ускользает - значение
\w
,\b
,\s
,\d
и их дополнений - не в Java распространяется на работу с Unicode. Один из них\b
имеет определенную расширенную семантику, но эти карты ни\w
, ни к идентификаторам Unicode , ни в Unicode свойство разрыва строки .Кроме того, свойства POSIX в Java доступны следующим образом:
Это настоящий бардак, потому что это означает , что вещи , как
Alpha
,Lower
иSpace
делать не на карте Java в UnicodeAlphabetic
,Lowercase
илиWhitespace
свойства. Это очень раздражает. Поддержка свойств Unicode в Java строго устаревшая. , я имею в виду, что она не поддерживает никаких свойств Unicode, за последнее десятилетие.Неспособность правильно говорить о пробелах очень раздражает. Рассмотрим следующую таблицу. Для каждой из этих кодовых точек существует столбец J-результатов для Java и столбец P-результатов для Perl или любого другого механизма регулярных выражений на основе PCRE:
Видеть, что?
Практически каждый из этих результатов пробелов Java - это «wr̲o̲n̲g̲» согласно Unicode. Это действительно большая проблема. Java просто запуталась, давая «неправильные» ответы согласно существующей практике, а также согласно Unicode. Кроме того, Java даже не дает вам доступа к реальным свойствам Unicode! Фактически, Java не поддерживает никаких свойств, соответствующих пробелам Unicode.
Решение всех этих проблем и не только
Чтобы справиться с этой и многими другими связанными проблемами, вчера я написал функцию Java для перезаписи строки шаблона, которая перезаписывает эти 14 экранирований классов символов:
заменив их на вещи, которые действительно работают в соответствии с Unicode предсказуемым и последовательным образом. Это всего лишь альфа-прототип от одного сеанса взлома, но он полностью функциональный.
Вкратце, мой код переписывает эти 14 следующим образом:
Некоторые моменты, которые следует учитывать ...
Которая использует для его
\X
определения , что Unicode теперь ссылается как наследие графем кластера , а не как расширенный кластера графемы , так как последний довольно сложнее. Сам Perl теперь использует более изящную версию, но старая версия по-прежнему отлично работает в наиболее распространенных ситуациях. РЕДАКТИРОВАТЬ: см. Приложение внизу.Что делать,
\d
зависит от вашего намерения, но по умолчанию используется определение Uniode. Я могу видеть , что люди не всегда хотят\p{Nd}
, но иногда либо[0-9]
или\pN
.Два определения границ
\b
и\B
специально написаны для использования этого\w
определения.Это
\w
определение слишком широкое, потому что оно охватывает буквы в паренде, а не только обведенные. Свойство UnicodeOther_Alphabetic
недоступно до JDK7, так что это лучшее, что вы можете сделать.Изучение границ
Границы были проблемы с тех пор Ларри Уолл первый придумал
\b
и\B
синтаксис говорить о них Perl 1.0 в 1987 году Ключ к пониманию того, как\b
и\B
оба работают, чтобы развеять два широко распространенных мифов о них:\w
символов слова, никогда для символов без слов.А
\b
граничные средства:И все это совершенно четко определяется как:
(?<=\w)
.(?=\w)
.(?<!\w)
.(?!\w)
.Следовательно, поскольку в регулярных выражениях
IF-THEN
кодируется какand
ed-togetherAB
, тоor
естьX|Y
, а посколькуand
приоритет вышеor
, чем , то это простоAB|CD
. Итак, все,\b
что означает границу, можно безопасно заменить на:с
\w
определенным соответствующим образом.(Вы можете подумать , что странно , что
A
иC
компоненты противоположны В идеальном мире, вы должны быть в состоянии написать это.AB|D
, Но на некоторое время я гоняться взаимного исключения противоречий в свойствах Unicode - которые я думаю , я позаботилась о , но на всякий случай я оставил двойное условие в границе. Плюс это делает его более расширяемым, если позже у вас появятся дополнительные идеи.)Для
\B
неграниц логика такова:Разрешить
\B
замену всех экземпляров на:Вот уж как
\b
и\B
ведут себя. Эквивалентные модели для них\b
использование((IF)THEN|ELSE)
конструкции(?(?<=\w)(?!\w)|(?=\w))
\B
использование((IF)THEN|ELSE)
конструкции(?(?=\w)(?<=\w)|(?<!\w))
Но версии с просто
AB|CD
прекрасны, особенно если в вашем языке регулярных выражений нет условных шаблонов, таких как Java. ☹Я уже проверил поведение границ, используя все три эквивалентных определения с помощью набора тестов, который проверяет 110 385 408 совпадений за запуск и который я использовал для десятка различных конфигураций данных в соответствии с:
Однако люди часто хотят границ другого типа. Им нужно что-то, что учитывает пробелы и края строки:
(?:(?<=^)|(?<=\s))
(?=$|\s)
Исправление Java с помощью Java
Код, который я опубликовал в другом ответе, предоставляет это и несколько других удобств. Сюда входят определения слов на естественном языке, дефисов, дефисов и апострофов, а также многое другое.
Он также позволяет указывать символы Юникода в логических кодовых точках, а не в идиотских суррогатах UTF-16. Трудно переоценить, насколько это важно! И это только для расширения строки.
Для замены charclass регулярного выражения, которая заставляет charclass в ваших регулярных выражениях Java, наконец, работать с Unicode и работать правильно, возьмите полный исходный код отсюда . Вы, конечно, можете поступать с ним как хотите. Если вы исправите это, я бы хотел услышать об этом, но вам не обязательно. Это довольно коротко. Суть основной функции перезаписи регулярных выражений проста:
Во всяком случае, этот код - всего лишь альфа-версия, которую я взломал на выходных. Так не останется.
Для бета-тестирования я намерен:
сложите дублирование кода
обеспечить более понятный интерфейс, касающийся неэкранированных переходов строки по сравнению с расширяющими escape-символами регулярных выражений
обеспечить некоторую гибкость в
\d
расширении, и, возможно,\b
предоставить удобные методы, которые обрабатывают поворот и вызывают Pattern.compile или String.matches или еще что-то для вас
Для производственного выпуска он должен содержать javadoc и набор тестов JUnit. Я могу включить свой гигатестер, но он не написан как тесты JUnit.
добавление
У меня есть хорошие новости и плохие новости.
Хорошая новость заключается в том, что теперь у меня есть очень близкое приближение к расширенному кластеру графем, которое можно использовать для улучшения
\X
.Плохая новость заключается в следующем:
который в Java вы бы написали как:
¡Tschüß!
источник
t
бросайте @tchrist. Это может ударить мне в голову. :)Очень жаль, что
\w
это не работает. Предлагаемое решение\p{Alpha}
у меня тоже не работает.Кажется,
[\p{L}]
ловит все буквы Юникода. Таким образом, Unicode-эквивалент\w
должен быть[\p{L}\p{Digit}_]
.источник
\w
также соответствует цифрам и многому другому. Думаю, для писем\p{L}
подойдет.\p{L}
достаточно. Также я думал, что проблема только в письмах.[\p{L}\p{Digit}_]
должен улавливать все буквенно-цифровые символы, включая подчеркивание.\w
Unicode определяется как нечто гораздо более широкое, чем просто\pL
цифры ASCII и все прочие глупости. Вы должны написать,[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]
если хотите, чтобы\w
Java поддерживала Unicode - или вы можете просто использовать моюunicode_charclass
функцию отсюда . Сожалею!\pL
таки работает (однобуквенный реквизит использовать не обязательно). Однако вы редко этого хотите, потому что вы должны быть достаточно осторожны, чтобы ваш матч не получил разных ответов только потому, что ваши данные находятся в форме D нормализации Unicode (также известной как NFD, что означает каноническое разложение ), а не в NFC (NFD с последующим каноническим состав ). Примером может служить кодовая точка U + E9 ("é"
)\pL
в форме NFC, но ее форма NFD становится U + 65.301, поэтому совпадает\pL\pM
. Вы можете любопытное обойти это с\X
:(?:(?=\pL)\X)
, но вам нужна моя версия , что для Java. :(В Java
\w
и\d
не поддерживает Unicode; они соответствуют только символам ASCII[A-Za-z0-9_]
и[0-9]
. То же самое\p{Alpha}
и с друзьями (предполагается, что «классы символов» POSIX, на которых они основаны, зависят от локали, но в Java они всегда сопоставляли только символы ASCII). Если вы хотите сопоставить «символы слова» Unicode, вы должны написать это по буквам, например[\pL\p{Mn}\p{Nd}\p{Pc}]
, для букв, модификаторов без пробелов (акцентов), десятичных цифр и соединительных знаков препинания.Однако, в Java
\b
является Unicode здравого смысла; он также используетCharacter.isLetterOrDigit(ch)
и проверяет наличие букв с диакритическими знаками, но единственный «соединительный знак препинания», который он распознает, - это подчеркивание. РЕДАКТИРОВАТЬ: когда я пробую ваш образец кода, он распечатывается""
иélève"
как следует ( см. Его на ideone.com ).источник
\b
поддерживает Unicode. Он делает массу ошибок."\u2163="
,"\u24e7="
И"\u0301="
все не подходящий шаблон"\\b="
в Java, но предполагается , чтобы , - какperl -le 'print /\b=/ || 0 for "\x{2163}=", "\x{24e7}=", "\x{301}="'
показывает. Однако, если (и только если) вы замените мою версию границы слова вместо родной\b
в Java, тогда все они будут работать и в Java.\b
правильность, просто указал, что он работает с символами Unicode (как реализовано в Java), а не только с ASCII-подобными\w
и друзьями. Однако он действительно работает правильно,\u0301
когда этот символ сочетается с базовым символом, как вe\u0301=
. И я не уверен, что Java ошибается в данном случае. Как комбинирующий знак может считаться символом слова, если он не является частью графемного кластера с буквой?\X
обозначает немаркировку, за которой следует любое количество отметок, проблематично, потому что вы должны иметь возможность описывать все файлы как совпадающие/^(\X*\R)*\R?$/
, но вы не можете, если у вас есть знак\pM
в начале файл или даже строку. Поэтому они расширили его, чтобы всегда соответствовать хотя бы одному символу. Так было всегда, но теперь он заставляет работать вышеуказанный шаблон. [… Продолжение…]\b
частично поддерживает Unicode , приносит больше вреда, чем пользы . Рассмотрите возможность сопоставления строки"élève"
с шаблоном\b(\w+)\b
. Видите проблему?\w+
находит два совпадения:l
иve
, что достаточно плохо. Но с границами слов ничего не находит, потому что\b
распознаетé
иè
как символы слова. Как минимум,\b
и\w
следует договориться о том, что такое словесный символ, а что нет.