Мне нужна помощь, чтобы завершить мою идею о регулярных выражениях.
Введение
Был вопрос о лучшем синтаксисе для регулярных выражений в 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
имеет смысл, но расширение тильды - нет. Может быть, должен быть какой-то отдельный синтаксис для определения собственных классов символов?
Можете ли вы привести примеры, когда названный шаблон очень полезен или вообще не полезен? Мне понадобятся несколько случаев и некоторые идеи для улучшения.
Реакция на ответ Триста
Комментарии к его возражениям
- Отсутствие строк из нескольких строк.
- В Java нет многострочных строк, которые я бы хотел изменить, но не могу.
- Свобода от безумно обременительной и подверженной ошибкам двойной обратной косой черты...
- Это опять то, что я не могу сделать, я могу только предложить обходной путь, с.ниже.
- Отсутствие исключений времени компиляции для недопустимых литералов регулярных выражений и отсутствие кэширования во время компиляции правильно скомпилированных литералов регулярных выражений.
- Поскольку регулярные выражения являются лишь частью стандартной библиотеки, а не самого языка, здесь ничего нельзя сделать.
- Нет средств отладки или профилирования.
- Здесь я ничего не могу сделать.
- Отсутствие соответствия 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-движоккак есть.