Из вашего отредактированного примера теперь я вижу, что вы хотели бы.И у вас есть мои симпатии в этом тоже.Регулярные выражения Java - это длинный, длинный и длинный путь от удобства, которое вы найдете в Ruby или Perl.И они почти всегда будут;это не может быть исправлено, поэтому мы застряли с этим беспорядком навсегда - по крайней мере, в Java.Другие языки JVM лучше справляются с этой задачей, особенно Groovy.Но они по-прежнему страдают некоторыми из присущих им недостатков и могут зайти так далеко.
С чего начать?Существуют так называемые удобные методы класса String: matches
, replaceAll
, replaceFirst
и split
.Иногда это может быть хорошо в небольших программах, в зависимости от того, как вы их используете.Тем не менее, у них действительно есть несколько проблем, которые, по-видимому, вы обнаружили.Вот неполный список этих проблем, и что можно и что нельзя делать с ними.
Метод неудобства очень странно называют «совпадением», но он требует, чтобы вы добавили свое регулярное выражение в обастороны, чтобы соответствовать всей строке.Этот нелогичный смысл противоречит любому значению слова «совпадение», используемому на любом предыдущем языке, и постоянно кусает людей.Шаблоны, переданные в другие 3 метода неудобств, работают очень непохоже на этот, потому что в других 3 они работают как обычные шаблоны, которые работают везде;только не в matches
.Это означает, что вы не можете просто копировать свои шаблоны, даже внутри методов в том же проклятом классе, ради всего святого!И нет никакого удобного метода find
, чтобы делать то, что делает любой другой сопоставитель в мире.Метод matches
должен был называться примерно как FullMatch
, и в класс String должен был быть добавлен метод PartialMatch
или find
.
НетAPI, который позволяет передавать Pattern.compile
флаги вместе со строками, которые вы используете для 4-х связанных с шаблоном вспомогательных методов класса String.Это означает, что вы должны полагаться на строковые версии, такие как (?i)
и (?x)
, но они не существуют для всех возможных флагов компиляции Pattern.Это крайне неудобно, если не сказать больше.
Метод split
не возвращает тот же результат в крайних случаях, что и split
в языках, из которых заимствована Java.Это подлый маленький гоча.Сколько элементов вы считаете нужным вернуться в список возврата, если вы разбили пустую строку, а?Java производит фальшивый возвратный элемент там, где он должен быть, а это значит, что вы не можете отличить законные результаты от поддельных.Это серьезный конструктивный недостаток, заключающийся в ":"
, вы не можете определить разницу между входами ""
против ":"
.Оу, ну и дела!Разве люди никогда не проверяют это?И снова, сломанное и принципиально ненадежное поведение невозможно исправить: вы никогда не должны менять вещи, даже сломанные вещи.Нельзя ломать сломанные вещи в Java, как это нигде.Сломанный навсегда здесь.
BackslЗональная запись регулярных выражений конфликтует с обратной косой чертой, используемой в строках.Это делает его сверхпопулярным неловким и подверженным ошибкам, потому что вы должны постоянно добавлять множество обратных косых черт ко всему, и слишком легко забыть один и не получить ни предупреждения, ни успеха.Простые шаблоны, такие как \b\w+\b
, превращаются в кошмары в типографском избытке: "\\b\\w+\\b"
.Удачи с чтением этого.Некоторые люди используют функцию косой черты в своих шаблонах, чтобы вместо этого записать ее как "/b/w+/b"
.Кроме чтения в ваших шаблонах из строки, нет никакого способа построить ваш шаблон в буквальном смысле WYSIWYG;это всегда тяжело с обратной косой чертой.Вы получили их все, и достаточно, и в нужных местах?Если так, то это действительно очень трудно читать.Если это не так, вы, вероятно, не получили их всех.По крайней мере, языки JVM, такие как Groovy, нашли здесь правильный ответ: дайте людям регулярные выражения первого класса, чтобы вы не сходили с ума. Вот большая коллекция примеров регулярных выражений Groovy , показывающая, насколько просто оно может и должно быть.
Режим (?x)
глубоко ошибочен.Комментарии принимаются не в стиле Java // COMMENT
, а в стиле оболочки # COMMENT
.Это не работает с многострочными строками.Он не принимает литералы как литералы, что вызывает проблемы с обратной косой чертой, перечисленные выше, что в корне компрометирует любую попытку выстроить линию, например, когда все комментарии начинаются в одном столбце.Из-за обратной косой черты, вы либо заставляете их начинаться с того же столбца в строке исходного кода, а затем перепутываете их, если распечатываете, или наоборот.Так много для разборчивости!
Невероятно сложно - и, по сути, неразборчиво - ввести символы Юникода в регулярное выражение.Нет поддержки символов с символическими именами, таких как \N{QUOTATION MARK}
, \N{LATIN SMALL LETTER E WITH GRAVE}
или \N{MATHEMATICAL BOLD CAPITAL C}
.Это означает, что вы застряли с неуправляемыми магическими числами.И вы даже не можете ввести их по коду.Вы не можете использовать \u0022
для первого, потому что препроцессор Java делает это синтаксической ошибкой.Итак, вместо этого вы переходите на \\u0022
, который работает, пока не дойдете до следующего, \\u00E8
, который не может быть введен таким образом, или он не сломает флаг CANON_EQ
.И последний из них - настоящий кошмар: его кодовая точка U + 1D402, но Java не поддерживает полный набор Unicode, использующий их номера кодовых точек в регулярных выражениях, что вынуждает вас воспользоваться калькулятором, чтобы выяснить, что это \uD835\uDC02
или \\uD835\\uDC02
(но не \\uD835\uDC02
), достаточно безумно.Но вы не можете использовать их в классах символов из-за ошибки проектирования, что делает невозможным сопоставление, скажем, [\N{MATHEMATICAL BOLD CAPITAL A}-\N{MATHEMATICAL BOLD CAPITAL Z}]
, потому что компилятор regex облажается на UTF-16.Опять же, это никогда не может быть исправлено, или это изменит старые программы.Вы даже не можете обойти ошибку, используя обычный обходной путь к проблемам Unicode-in-source-кода в Java, компилируя с java -encoding UTF-8
, потому что глупая вещь хранит строки как неприятный UTF-16, который обязательно разбивает их в символьных классах. Упс!
Многие из регулярных выражений, на которые мы привыкли полагаться в других языках, отсутствуют в Java.Для примеров нет ни именованных групп, ни даже относительно пронумерованных.Это делает построение больших шаблонов из меньших по своей природе подверженным ошибкам.Существует интерфейсная библиотека, которая позволяет вам иметь простые именованные группы, и, действительно, это, наконец, появится в рабочей JDK7.Но даже в этом случае нет механизма для того, что делать с более чем одной группой с одним и тем же именем.И у вас все еще нет относительно пронумерованных буферов.Мы снова вернулись к плохим старым дням, вещи, которые были решены давным-давно.
Нет поддержки последовательности разрыва строки, которая является одной из двух только «Настоятельно рекомендуемых» частейстандарта, который предлагает использовать \R
для таких целей.Это неудобно для эмуляции из-за природы переменной длины и отсутствия поддержки Java графами.
Экранирование класса символовs не работают с собственным набором символов Java!Да, все верно: такие рутинные вещи, как \w
и \s
(точнее, "\\w"
и "\\b"
) не работают на Unicode в Java!Это не классный вид ретро.Что еще хуже, Java \b
(то есть "\\b"
, что не то же самое, что "\b"
) имеет некоторую чувствительность к Unicode, хотя не то, что стандарт говорит, что должно быть.Так, например, строка типа "élève"
в Java никогда не будет соответствовать шаблону \b\w+\b
, и не только целиком на Pattern.matches
, но на самом деле ни при каких условиях , как вы можете получить из Pattern.find
,Это просто так облажалось, что нельзя верить.Они нарушили внутреннюю связь между \w
и \b
, а затем неправильно определили их для загрузки !!Он даже не знает, что такое буквенные коды Unicode.Это в высшей степени нарушено, и они никогда не смогут это исправить, потому что это изменит поведение существующего кода, что строго запрещено во вселенной Java.Лучшее, что вы можете сделать, - это создать библиотеку перезаписи, которая будет работать в качестве внешнего интерфейса, прежде чем перейдет к фазе компиляции;таким образом вы можете принудительно перенести ваши шаблоны из 1960-х в 21-й век обработки текста.
Поддерживаются только два свойства Юникода - Общие категории и Свойства блока.Свойства общей категории поддерживают только такие сокращения, как \p{Sk}
, в отличие от строгой рекомендации стандартов, разрешающей также \p{Modifier Symbol}
, \p{Modifier_Symbol}
и т. Д. Вы даже не получите требуемых псевдонимов, которые, как говорится в стандарте, должны быть.Это делает ваш код еще более нечитаемым и не поддерживаемым.Вы наконец получите поддержку свойства Script в производственном JDK7, но это все еще серьезно не соответствует минимальному набору из 11 основных свойств, который Стандарт говорит, что вы должны обеспечить даже минимальноеуровень поддержки Unicode.
Некоторые из скудных свойств, которые предоставляет Java: faux amis : они имеют те же имена, что и официальные имена свойств Unicode, , но они вообще что-то делаютотличается .Например, Unicode требует, чтобы \p{alpha}
было таким же, как \p{Alphabetic}
, но Java делает его только архаичной и более не причудливой 7-битной алфавитной последовательностью, что на 4 порядка меньше.Пробелы - это еще один недостаток, так как вы используете версию Java, которая маскируется под пробелы Unicode, ваши парсеры UTF-8 будут ломаться из-за их кодовых точек NO-BREAK SPACE, которые Unicode нормативно требуют, чтобы считаться пробелами, но Java игнорирует это требование, поэтому нарушаетваш синтаксический анализатор.
Графемы не поддерживаются, как обычно \X
.Это делает невозможным бесчисленное множество общих задач, которые вам нужны и которые вы хотите выполнять с помощью регулярных выражений.Мало того, что расширенные кластеры графем находятся вне вашей досягаемости, поскольку Java не поддерживает почти ни одно из свойств Unicode, вы даже не можете приблизиться к старым устаревшим кластерам графем , используя стандарт (?:\p{Grapheme_Base}\p{Grapheme_Extend}]*)
.Неспособность работать с графемами делает невозможной даже простейшую обработку текста в Юникоде.Например, вы не можете сопоставить гласный независимо от диакритического знака в Java.Способ, которым вы делаете это на языке с поддержкой графем, варьируется, но, по крайней мере, вы должны быть в состоянии бросить это в NFD и сопоставить (?:(?=[aeiou])\X)
.В Java вы не можете сделать даже так много: графемы за пределами вашей досягаемости.А это значит, что Java не может даже обрабатывать свой собственный набор символов.Он дает вам Unicode, а затем делает невозможным работу с ним.
ThУдобные методы в классе String не кэшируют скомпилированное регулярное выражение.На самом деле, не существует такой вещи, как шаблон времени компиляции, который проверяется на синтаксис во время компиляции - , когда предполагается, что проверка синтаксиса происходит. Это означает, что ваша программа, которая использует только постоянные регулярные выраженияПолностью понятный во время компиляции, будет разорван с исключением во время его выполнения, если вы забудете небольшую обратную косую черту здесь или там, как обычно, из-за ранее рассмотренных недостатков.Даже Groovy правильно понимает эту часть.Регулярные выражения являются слишком высокоуровневой конструкцией, чтобы иметь дело с неприятной постфактумной моделью Java, скрытой от фактов, и они слишком важны для обычной обработки текста, чтобы их игнорировать.Java - слишком низкоуровневый язык для этого материала, и он не в состоянии предоставить простую механику, из которой вы сами можете построить то, что вам нужно: вы не можете получить это отсюда.
Классы String
и Pattern
помечены final
в Java.Это полностью убивает любую возможность использования правильного дизайна ОО для расширения этих классов.Вы не можете создать лучшую версию метода matches
путем создания подклассов и замены.Черт возьми, ты не можешь даже подкласс!Финал не является решением;final - это смертный приговор, от которого нет апелляции.
Наконец, , чтобы показать вам, насколько действительно регулярные выражения Java повреждены мозгом, рассмотрим этот многострочный шаблон,который показывает многие из уже описанных недостатков:
String rx =
"(?= ^ \\p{Lu} [_\\pL\\pM\\d\\-] + \$)\n"
. " # next is a big can't-have set \n"
. "(?! ^ .* \n"
. " (?: ^ \\d+ $ \n"
. " | ^ \\p{Lu} - \\p{Lu} $ \n"
. " | Invitrogen \n"
. " | Clontech \n"
. " | L-L-X-X # dashes ok \n"
. " | Sarstedt \n"
. " | Roche \n"
. " | Beckman \n"
. " | Bayer \n"
. " ) # end alternatives \n"
. " \\b # only on a word boundary \n"
. ") # end negated lookahead \n"
;
Вы видите, насколько это неестественно?Вы должны поместить буквальные переводы строк в свои строки;вы должны использовать не Java-комментарии;вы не можете ничего сделать из-за дополнительной обратной косой черты;Вы должны использовать определения вещей, которые не работают правильно на Unicode.Помимо этого есть еще много проблем.
Мало того, что не планируется исправлять почти любые из этих серьезных недостатков, на самом деле невозможно вообще исправить почти любые из них, потому что вы меняете старые программы.Даже обычные инструменты OO-дизайна запрещены, потому что все они связаны с окончательностью смертного приговора и не могут быть исправлены.
Так что Alireza Noori, если вы чувствуете, что неуклюжие регулярные выражения Java слишком заняты длянадежная и удобная обработка регулярных выражений когда-либо возможна в Java, я не могу отрицать вас.Извините, но так оно и есть.
«Исправлено в следующем выпуске!»
То, что некоторые вещи не могут быть исправлены, не означает, что ничто не может быть исправлено.Это нужно сделать очень осторожно.Вот вещи, которые я знаю, которые уже исправлены в текущей JDK7 или предлагаемых сборках JDK8:
Теперь поддерживается свойство Unicode Script.Вы можете использовать любую из эквивалентных форм \p{Script=Greek}
, \p{sc=Greek}
, \p{IsGreek}
или \p{Greek}
.Это по своей сути превосходит старые неуклюжие свойства блока.Это означает, что вы можете делать что-то вроде [\p{Latin}\p{Common}\p{Inherited}]
, что очень важно.
Ошибка UTF-16 имеет обходной путь.Теперь вы можете указать любую кодовую точку Юникода по ее номеру, используя нотацию \x{⋯}
, например \x{1D402}
.Это работает даже внутри классов персонажей, наконец, позволяя [\x{1D400}-\x{1D419}]
работать правильно.Вы все равно должны удвоить обратную косую черту, хотя, и это работает только в regexex, а не в строках вообще, как это действительно должно быть.
Именованные группы теперь поддерживаются через стандартную запись (?<NAME>⋯)
длясоздайте его и \k<NAME>
чтобы сделать ссылку на него.Они также способствуют числовым номерам групп.Однако вы не можете получить более одного из них в одном и том же шаблоне, а также не можете использовать их для рекурсии.
Новый флаг компиляции Pattern, Pattern.UNICODE_CHARACTER_CLASSES
и связанный с ним встраиваемый ключ,(?U)
, теперь будет обмениваться всеми определениями таких вещей, как \w
, \b
, \p{alpha}
и \p{punct}
, так что теперь они соответствуют определениям тех вещей, которые требуются в стандарте Unicode.
Отсутствует илиошибочно заданные двоичные свойства \p{IsLowercase}
, \p{IsUppercase}
и \p{IsAlphabetic}
теперь будут поддерживаться, и они соответствуют методам в классе Character
. Это важно, потому что Unicode делает существенное и широко распространенное различие между простыми буквами и буквенными или буквенными кодовыми точками. Эти ключевые свойства входят в число 11 основных свойств, которые абсолютно необходимы для соответствия Level 1 UTS # 18, «Регулярные выражения Unicode» , без которого вы действительно не сможете работать с Unicode.
Эти улучшения и исправления очень важны, чтобы наконец их иметь, и поэтому я рад, даже рад, что они у них есть.
Но я не буду использовать Java для промышленных, современных приложений для регулярных выражений и / или Unicode. Просто слишком много не хватает в модели Unicode, которая все еще нестабильна после 20 лет Java, чтобы выполнить настоящую работу, если вы решитесь использовать набор символов, который дает Java. И модель с болтовым креплением никогда не работает , а это все регулярные выражения Java. Вы должны начать с первых принципов, как это сделал Groovy.
Конечно, это могло бы работать для очень ограниченных приложений, чья небольшая клиентская база ограничена англоговорящими моноглотами в сельской Айове без внешнего взаимодействия или какой-либо потребности в символах, помимо того, что мог послать телеграф старого стиля. Но для скольких проектов действительно ли верно? Оказывается, даже меньше, чем ты думаешь.
Именно по этой причине определенный (и очевидный) многомиллиардный доллар только что отменил международное развертывание важного приложения. Поддержка Unicode в Java - не только в регулярных выражениях, но и повсюду - оказалась слишком слабой для необходимой надежной интернационализации в Java. Из-за этого они были вынуждены перейти от первоначально запланированного развертывания по всему миру к простому развертыванию в США. Это положительно местничество. И нет, есть Nᴏᴛ Hᴀᴘᴘʏ; ты будешь?
У Java было 20 лет, чтобы сделать это правильно, и они явно пока не сделали этого, поэтому я не мог задержать дыхание. Или бросать хорошие деньги за плохими; урок здесь состоит в том, чтобы игнорировать ажиотаж и вместо этого применить должную осмотрительность, чтобы очень был уверен, что вся необходимая инфраструктурная поддержка есть до , и вы слишком много инвестируете. В противном случае вы тоже можете застрять без каких-либо реальных опций, если слишком далеко зайдете, чтобы спасти свой проект.
Предостережение Emptor