Лучшие идеи синтаксиса регулярных выражений - PullRequest
7 голосов
/ 06 февраля 2011

Мне нужна помощь, чтобы завершить мою идею о регулярных выражениях.

Введение

Был вопрос о лучшем синтаксисе для регулярных выражений в SE, но я этого не делаюдумаю, я бы использовал свободный синтаксис.Это, конечно, хорошо для новичков, но в случае сложного регулярного выражения вы заменяете строку тарабарщины на целую страницу немного лучшего тарабарщины.Мне нравится подход Мартина Фаулера , в котором регулярное выражение состоит из более мелких частей.Его решение читабельно, но сделано вручную;он предлагает умный способ построить сложное регулярное выражение вместо класса, поддерживающего его.

Я пытаюсь сделать это в классе, используя что-то вроде (сначала посмотрите его пример)

final MyPattern pattern = MyPattern.builder()
.caseInsensitive()
.define("numberOfPoints", "\\d+")
.define("numberOfNights", "\\d+")
.define("hotelName", ".*")
.define(' ', "\\s+")
.build("score `numberOfPoints` for `numberOfNights` nights? at `hotelName`");

MyMatcher m = pattern.matcher("Score 400 FOR 2 nights at Minas Tirith Airport");
System.out.println(m.group("numberOfPoints")); // prints 400

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

  • определяет именованные шаблоны и использует их, заключая в обратные галочки
    • `name` создает именованную группу
      • мнемоника: оболочка захватывает результат команды, заключенной в обратные кавычки
    • `:name` создает группу без захвата
      • мнемоника: аналогично (?: ...)
    • `-name` создает обратную ссылку
      • мнемоника: тире соединяет его с предыдущим вхождением
  • переопределять отдельные символы и использовать их везде, если не указано
    • здесь разрешены только некоторые символы (например, ~ @#% ")
      • переопределение + или ( будеточень запутанно, поэтому нельзя
      • переопределятьпробел, означающий любой интервал, очень естественен в приведенном выше примере
      • переопределение символа может сделать шаблон более компактным, что хорошо, если не использовать чрезмерно
      • , например использовать что-то вроде define('#', "\\\\") для сопоставления обратной косой чертыможет сделать шаблон более читабельным
  • переопределить некоторые цитируемые последовательности, такие как \s или \w
    • стандартные определения неСоответствие Unicode
    • иногда у вас может быть собственное представление о том, что такое слово или пробел

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

Вопросы

Вышеприведенное не должно быть сложным для реализации (я уже сделал большую часть из этого) и может быть действительно полезным, я надеюсь. Вы так думаете?

Однако я не уверен, как он должен вести себя в скобках, иногда имеет смысл использовать определения, а иногда нет, например, в

.define(' ', "\\s")            // a blank character
.define('~', "/\**[^*]+\*/")   // an inline comment (simplified)
.define("something", "[ ~\\d]")

расширение пространства до \s имеет смысл, но расширение тильды - нет. Может быть, должен быть какой-то отдельный синтаксис для определения собственных классов символов?

Можете ли вы привести примеры, когда названный шаблон очень полезен или вообще не полезен? Мне понадобятся несколько случаев и некоторые идеи для улучшения.

Реакция на ответ Триста

Комментарии к его возражениям

  1. Отсутствие строк из нескольких строк.
    • В Java нет многострочных строк, которые я бы хотел изменить, но не могу.
  2. Свобода от безумно обременительной и подверженной ошибкам двойной обратной косой черты...
    • Это опять то, что я не могу сделать, я могу только предложить обходной путь, с.ниже.
  3. Отсутствие исключений времени компиляции для недопустимых литералов регулярных выражений и отсутствие кэширования во время компиляции правильно скомпилированных литералов регулярных выражений.
    • Поскольку регулярные выражения являются лишь частью стандартной библиотеки, а не самого языка, здесь ничего нельзя сделать.
  4. Нет средств отладки или профилирования.
    • Здесь я ничего не могу сделать.
  5. Отсутствие соответствия UTS # 18.
    • Это может быть легко решено путем переопределения соответствующих шаблонов, как я предлагал.Это не идеально, так как в отладчике вы увидите взорванные замены.

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

RFC 5322

Ваш пример может быть легко написан с использованием моего синтаксиса:

final MyPattern pattern = MyPattern.builder()
.define(" ", "") // ignore spaces
.useForBackslash('#') // (1): see (2)
.define("address",         "`mailbox` | `group`")
.define("WSP",             "[\u0020\u0009]")
.define("DQUOTE",          "\"")
.define("CRLF",            "\r\n")
.define("DIGIT",           "[0-9]")
.define("ALPHA",           "[A-Za-z]")
.define("NO_WS_CTL",       "[\u0001-\u0008\u000b\u000c\u000e-\u001f\u007f]") // No whitespace control
...
.define("domain_literal",  "`CFWS`? #[ (?: `FWS`? `dcontent`)* `FWS`? #] `CFWS1?") // (2): see (1)
...
.define("group",           "`display_name` : (?:`mailbox_list` | `CFWS`)? ; `CFWS`?")
.define("angle_addr",      "`CFWS`? < `addr_spec` `CFWS`?")
.define("name_addr",       "`display_name`? `angle_addr`")
.define("mailbox",         "`name_addr` | `addr_spec`")
.define("address",         "`mailbox` | `group`")
.build("`address`");

Недостатки

При переписывании вашего примера я столкнулся со следующими проблемами:

  • Поскольку нет \xdd escape-последовательностей \udddd необходимо использовать
  • Использование другого символа вместо обратной косой черты немногостранно
  • Поскольку я предпочитаю писать это снизу вверх, мне пришлось перевернуть ваши строки
  • Без особого представления, что он делает, я, кроме себя, сделал несколько ошибок

С другой стороны: - игнорирование пробелов не проблема - комментарии не проблема - удобочитаемость

И самое важное: Это простая Java и использует существующий regex-движоккак есть.

Ответы [ 2 ]

3 голосов
/ 06 февраля 2011

Примеры именованного захвата

Можете ли вы вспомнить некоторые примеры, когда именованный шаблон очень полезен или вообще не полезен?

В ответ на ваш вопрос, вот пример, где именованные шаблоны особенно полезны.Это шаблон Perl или PCRE для анализа почтового адреса RFC 5322.Во-первых, он находится в режиме /x в силу (?x).Во-вторых, он отделяет определения от вызова;именованная группа address - это то, что выполняет полный анализ рекурсивного спуска.Его определение следует за ним в неисполняющемся блоке (?DEFINE)…).

   (?x)              # allow whitespace and comments

   (?&address)       # this is the capture we call as a "regex subroutine"

   # the rest is all definitions, in a nicely BNF-style
   (?(DEFINE)

     (?<address>         (?&mailbox) | (?&group))
     (?<mailbox>         (?&name_addr) | (?&addr_spec))
     (?<name_addr>       (?&display_name)? (?&angle_addr))
     (?<angle_addr>      (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
     (?<group>           (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ; (?&CFWS)?)
     (?<display_name>    (?&phrase))
     (?<mailbox_list>    (?&mailbox) (?: , (?&mailbox))*)

     (?<addr_spec>       (?&local_part) \@ (?&domain))
     (?<local_part>      (?&dot_atom) | (?&quoted_string))
     (?<domain>          (?&dot_atom) | (?&domain_literal))
     (?<domain_literal>  (?&CFWS)? \[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
                                   \] (?&CFWS)?)
     (?<dcontent>        (?&dtext) | (?&quoted_pair))
     (?<dtext>           (?&NO_WS_CTL) | [\x21-\x5a\x5e-\x7e])

     (?<atext>           (?&ALPHA) | (?&DIGIT) | [!#\$%&'*+-/=?^_`{|}~])
     (?<atom>            (?&CFWS)? (?&atext)+ (?&CFWS)?)
     (?<dot_atom>        (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
     (?<dot_atom_text>   (?&atext)+ (?: \. (?&atext)+)*)

     (?<text>            [\x01-\x09\x0b\x0c\x0e-\x7f])
     (?<quoted_pair>     \\ (?&text))

     (?<qtext>           (?&NO_WS_CTL) | [\x21\x23-\x5b\x5d-\x7e])
     (?<qcontent>        (?&qtext) | (?&quoted_pair))
     (?<quoted_string>   (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
                          (?&FWS)? (?&DQUOTE) (?&CFWS)?)

     (?<word>            (?&atom) | (?&quoted_string))
     (?<phrase>          (?&word)+)

     # Folding white space
     (?<FWS>             (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
     (?<ctext>           (?&NO_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e])
     (?<ccontent>        (?&ctext) | (?&quoted_pair) | (?&comment))
     (?<comment>         \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) )
     (?<CFWS>            (?: (?&FWS)? (?&comment))*
                         (?: (?:(?&FWS)? (?&comment)) | (?&FWS)))

     # No whitespace control
     (?<NO_WS_CTL>       [\x01-\x08\x0b\x0c\x0e-\x1f\x7f])

     (?<ALPHA>           [A-Za-z])
     (?<DIGIT>           [0-9])
     (?<CRLF>            \x0d \x0a)
     (?<DQUOTE>          ")
     (?<WSP>             [\x20\x09])
   )

Я настоятельно рекомендую не изобретать совершенно хорошее колесо.Начните с того, чтобы стать PCRE-совместимым.Если вы хотите выйти за рамки базовых шаблонов Perl5, таких как синтаксический анализатор RFC5322 выше, всегда есть шаблоны Perl6 , на которые можно опираться.

Это действительно, действительно платит за исследованияв существующую практику и литературу, прежде чем отправиться в открытый R & D миссии.Все эти проблемы давно решены, иногда довольно элегантно.

Улучшение синтаксиса Java Regex

Если вы действительно хотите более совершенные идеи синтаксиса регулярных выражений для Java, вы должны сначала устранить эти специфические недостатки в регулярных выражениях Java.:

  1. Отсутствие многострочных шаблонных строк, как показано выше.
  2. Свобода от безумно обременительной и подверженной ошибкам двойной обратной косой черты, как также продемонстрировано выше.
  3. Отсутствиеисключений времени компиляции для недопустимых литералов регулярных выражений и отсутствия кэширования во время компиляции правильно скомпилированных литералов регулярных выражений.
  4. Невозможно изменить что-то вроде "foo".matches(pattern), чтобы использовать лучшую библиотеку шаблонов, частично, но не только из-заfinal классы, которые не могут быть переопределены.
  5. Нет средств отладки или профилирования.
  6. Отсутствие соответствия UTS # 18: поддержка базовых регулярных выражений , самых элементарных шагов, необходимых для того, чтобы сделать регулярные выражения Java полезными для Unicode,Их в настоящее время нет.Они даже не поддерживают свойства Unicode 3.1 десятилетия назад, что означает, что вы не можете использовать шаблоны Java для Unicode любым разумным способом;основные строительные блоки отсутствуют.

Из них первые 3 были адресованы на нескольких языках JVM, включая Groovy и Scala;даже Clojure идет туда-сюда.

Второй набор из 3 шагов будет более жестким, но абсолютно обязательным.Последнее, отсутствие даже самой базовой поддержки Unicode в регулярных выражениях, просто убивает Java для работы с Unicode.Это совершенно непростительно в конце игры.Я могу предоставить множество примеров, если это необходимо, но вы должны мне доверять, потому что я действительно знаю, о чем я говорю здесь.

Только после того, как вы выполнили все это, вы должны быть обеспокоены исправлением Java.регулярные выражения, чтобы они могли догнать текущий уровень техники в сопоставлении с образцом.До тех пор, пока вы не позаботитесь об этих прошлых упущениях, вы не сможете начать смотреть в настоящее, не говоря уже о будущем.

1 голос
/ 07 февраля 2011

Я думаю, что, возможно, Regular Expression на самом деле не то, что нужно, а скорее что-то вроде библиотеки Parser-Combinator (которая может работать с символами и / или включать регулярные выражения в свои конструкции).

То есть шаг за пределы области регулярных выражений (нерегулярно, насколько они могут быть реализованы - tchrist определенно наслаждается реализацией Perl ;-) и в контекстно-свободные грамматики - или впо крайней мере те, которые могут быть представлены в LL (n), предпочтительно с минимальным возвратом.

Scala: Волшебство Begind Parse-Combinators Обратите внимание, как это выглядит очень похоже на BCNF.Имеет хорошее введение.

Haskel: Parsec Ditto.

Некоторые примеры в Java: JParsec и JPC .

Java, как язык, однако, не так благоприятен для таких бесшовных расширений DSL, как некоторые конкуренты; -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...