Разработка альтернативного (свободного?) интерфейса для регулярных выражений

StackOverflow https://stackoverflow.com/questions/1579023

Вопрос

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

Я думал о том, как можно было бы исправить эту ситуацию.Пока что самая многообещающая идея, которая у меня есть, - это использование свободный интерфейс.Чтобы привести пример, вместо:

Pattern pattern = Pattern.compile("a*|b{2,5}");

можно было бы написать что-то вроде этого

import static util.PatternBuilder.*

Pattern pattern = string("a").anyTimes().or().string("b").times(2,5).compile();

Pattern alternative = 
  or(
    string("a").anyTimes(),
    string("b").times(2,5)
  )
  .compile();

В этом очень коротком примере общий способ создания регулярного выражения по-прежнему вполне понятен любому посредственному талантливому разработчику.Однако подумайте о тех жутких выражениях, которые заполняют две или более строк по 80 символов в каждой.Конечно, (подробный) беглый интерфейс потребовал бы нескольких строк вместо всего двух, но я уверен, что он был бы намного более читабельным (следовательно, ремонтопригодным).

Теперь мои вопросы:

  1. Знаете ли вы какой-нибудь подобный подход к регулярным выражениям?

  2. Согласны ли вы, что такой подход мог бы быть лучше, чем использование простых строк?

  3. Как бы вы спроектировали API?

  4. Использовали бы вы такую удобную утилиту в своих проектах?

  5. Как вы думаете, было бы интересно реализовать это?;)

Редактировать: Представьте, что могут существовать методы, которые находятся на более высоком уровне, чем простые конструкции, которые мы все используем в регулярных выражениях, например

// matches aaaab@example.com - think of it as reusable expressions
Pattern p = string{"a").anyTimes().string("b@").domain().compile();

РЕДАКТИРОВАТЬ - краткое изложение комментариев:

Интересно прочитать, что большинство людей думают, что регулярные выражения никуда не денутся, хотя нужны инструменты, чтобы их прочитать, и умные ребята, чтобы придумать способы сделать их удобными для обслуживания.Хотя я не уверен, что свободный интерфейс - лучший выход, я уверен, что некоторые умные инженеры - мы?;) - следует потратить некоторое время, чтобы сделать регулярные выражения делом прошлого - достаточно того, что они были с нами в течение 50 лет, не так ли?

ОТКРЫТАЯ НАГРАДА

Награда будет присуждена за лучшую идею (код не требуется) по новому подходу к регулярным выражениям.

РЕДАКТИРОВАТЬ - ХОРОШИЙ ПРИМЕР:

вот о каком паттерне я говорю - дополнительная благодарность первому, кто смог это перевести - разрешены RegexBuddies (кстати, это из проекта Apache) дополнительная похвала присуждена чии и mez:это шаблон проверки адреса электронной почты, совместимый с RFC, хотя его RFC822 (см . ex-parrot.com), а не 5322 - не уверен, есть ли разница, хотя - если да, я получу следующую дополнительную награду за патч для 5322 ;)

private static final String pattern = "(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:"
    + "\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:("
    + "?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ "
    + "\\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\0"
    + "31]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\"
    + "](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+"
    + "(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:"
    + "(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)"
    + "?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\"
    + "r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?["
    + " \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)"
    + "?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t]"
    + ")*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?["
    + " \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*"
    + ")(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)"
    + "*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+"
    + "|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r"
    + "\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:"
    + "\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t"
    + "]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031"
    + "]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\]("
    + "?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?"
    + ":(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?"
    + ":\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?"
    + ":(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?"
    + "[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*:(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|"
    + "\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>"
    + "@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\""
    + "(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?"
    + ":[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\["
    + "\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-"
    + "\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|("
    + "?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;"
    + ":\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[(["
    + "^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\""
    + ".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\"
    + "]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\"
    + "[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\"
    + "r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]"
    + "|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\0"
    + "00-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\"
    + ".|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,"
    + ";:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?"
    + ":[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:["
    + "^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]"
    + "]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)(?:,\\s*("
    + "?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:("
    + "?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=["
    + "\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t"
    + "])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t"
    + "])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?"
    + ":\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|"
    + "\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:"
    + "[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\"
    + "]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)"
    + "?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\""
    + "()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)"
    + "?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>"
    + "@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?["
    + " \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,"
    + ";:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:"
    + "\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\["
    + "\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])"
    + "*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])"
    + "+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\"
    + ".(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:("
    + "?:\\r\\n)?[ \\t])*))*)?;\\s*)";
Это было полезно?

Решение

Как бы вы спроектировали API?

Я бы позаимствовал страницу из API критериев гибернации.Вместо того, чтобы использовать:

string("a").anyTimes().or().string("b").times(2,5).compile()

Используйте шаблон, подобный:

Pattern.or(Pattern.anyTimes("a"), Pattern.times("b", 2, 5)).compile()

Это обозначение немного более краткое, и я чувствую, что так легче понять иерархию / структуру шаблона.Каждый метод может принимать либо строку, либо фрагмент шаблона в качестве первого аргумента.

Знаете ли вы какой-нибудь подобный подход к регулярным выражениям?

Не навскидку, нет.

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

Да, абсолютно...если вы используете регулярные выражения для чего-либо отдаленно сложного.Для очень коротких и простых случаев более удобна строка.

Использовали бы вы такую удобную утилиту в своих проектах?

Возможно, по мере того, как это стало проверенным / стабильным...включение этого в более крупный служебный проект, такой как Apache Commons, может быть плюсом.

Как вы думаете, было бы интересно реализовать это?;)

+1

Другие советы

Мартин Фаулер предлагает еще одна стратегия.А именно взять значимые части регулярного выражения и заменить их переменными.Он приводит следующий пример:

 "^score\s+(\d+)\s+for\s+(\d+)\s+nights?\s+at\s+(.*)" 

становится

   String scoreKeyword = "^score\s+";
   String numberOfPoints = "(\d+)";
   String forKeyword = "\s+for\s+";
   String numberOfNights = "(\d+)";
   String nightsAtKeyword = "\s+nights?\s+at\s+";
   String hotelName = "(.*)";

   String pattern = scoreKeyword + numberOfPoints +
      forKeyword + numberOfNights + nightsAtKeyword + hotelName;

Который гораздо более удобочитаем и ремонтопригоден.

Целью предыдущего примера было проанализировать numberOfPoints, numberOfNights и HotelName из списка строк типа:

score 400 for 2 nights at Minas Tirith Airport

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

Кроме того, я думаю, что вашу версию сложнее прочитать эксперту по регулярным выражениям.

Я бы рекомендовал аннотировать регулярное выражение следующим образом:

Pattern pattern = Pattern.compile(
  "a*     # Find 0 or more a        \n" +
  "|      # ... or ...              \n" +
  "b{2,5} # Find between 2 and 5 b  \n",
Pattern.COMMENTS);

Это легко читается для любого уровня подготовки, а для неопытных одновременно обучает регулярному выражению.Кроме того, комментарии могут быть адаптированы к ситуации, чтобы объяснить бизнес-правила, лежащие в основе регулярного выражения, а не просто структуру.

Кроме того, такой инструмент, как RegexBuddy Регулярное выражение могу взять ваше регулярное выражение и перевести его в:

Match either the regular expression below (attempting the next alternative only if this one fails) «a*»
   Match the character "a" literally «a*»
      Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
Or match regular expression number 2 below (the entire match attempt fails if this one fails to match) «b{2,5}»
   Match the character "b" literally «b{2,5}»
      Between 2 and 5 times, as many times as possible, giving back as needed (greedy) «{2,5}»

Это интригующая концепция, но в том виде, в каком она представлена, есть несколько недостатков.

Но сначала ответы на ключевые заданные вопросы:

Теперь мои вопросы:

1.Знаете ли вы какой-нибудь подобный подход к регулярным выражениям?

Нет ничего такого, о чем бы уже не упоминалось.И о тех, о ком я узнал, прочитав вопрос и ответы.

2.Согласны ли вы, что такой подход мог бы быть лучше, чем использование простых строк?

Если это работает так, как рекламируется, это определенно значительно упростит отладку.

3.Как бы вы спроектировали API?

Смотрите мои заметки в следующем разделе.Я беру ваши примеры и связанную библиотеку .NET в качестве отправной точки.

4.Использовали бы вы такую удобную утилиту в своих проектах?

Не определился.У меня нет проблем с работой с текущей версией cryptic regular expressions.И мне понадобился бы инструмент для преобразования существующих регулярных выражений в версию fluent language.

5.Как вы думаете, было бы интересно реализовать это?;)

Мне бы понравилось разрабатывать более высокий уровень "как", чем писать сам код.Что объясняет стену текста, которая является этим ответом.


Вот пара проблем, которые я заметил, и то, как я бы с этим справился.

Неясная структура.

Ваш пример, похоже, создает регулярное выражение путем объединения со строками.Это не очень надежно.Я считаю, что эти методы следует добавить к объектам String и Patern / Regex, потому что это сделает реализацию и код более чистыми.Кроме того, это похоже на классическое определение регулярных выражений.

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

Редактировать: Кажется, я повсюду использовал следующие соглашения.Итак, я прояснил их и перенес сюда.

  • Методы экземпляра:увеличение паттерна.например:фиксирование, повторение, осмотр утверждений.

  • Операторы:порядок выполнения операций.чередование, конкатенация

  • Константы:классы символов, границы (вместо \w, $, \b и т.д.)

Как будет осуществляться захват / кластеризация?

Захват - это огромная часть регулярных выражений.

Я вижу, что каждый объект шаблона хранится внутри в виде кластера.(?:pattern) в терминах Perl.Позволяет легко перемешивать жетоны с рисунком, не мешая другим изделиям.

Я ожидаю, что захват будет выполняться как метод экземпляра по шаблону.Берем переменную для хранения соответствующей строки [ов].

pattern.capture(variable) сохранял бы шаблон в переменной.В том случае, если захват является частью выражения, которое должно быть сопоставлено несколько раз, переменная должна содержать массив строк всех совпадений для шаблона.

Свободное владение языками может быть очень неоднозначным.

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

Делает

Pattern pattern = string("a").anyTimes().or().string("b").times(2,5).compile();

производить /a*|b{2,5}/ или /(a*|b){2,5}/ ?

Как бы такая схема обрабатывала вложенное чередование?Например: /a*|b(c|d)|e/

Я вижу три способа обработки чередования в регулярных выражениях

  1. В качестве оператора: pattern1 or pattern2 => pattern # /pattern1|pattern2/
  2. Как метод класса: Pattern.or( pattern1, pattern2[, pattern3]*) => pattern # /pattern1|patern2|patern3|...|/
  3. В качестве метода экземпляра: pattern1.or(pattern2) => pattern # /pattern1|patern2/

Я бы обработал конкатенацию таким же образом.

  1. В качестве оператора: pattern1 + pattern2 => pattern # /pattern1pattern2/
  2. Как метод класса: Pattern.concatenate( pattern1, pattern2[, pattern3]*) => pattern # /pattern1patern2patern3.../
  3. В качестве метода экземпляра: pattern1.then(pattern2) => pattern # /pattern1patern2/

Как расширить для часто используемых шаблонов

Используемая предложенная схема .domain() который, по-видимому, является обычным регулярным выражением.Рассмотрение пользовательских шаблонов как методов не позволяет легко добавлять новые шаблоны.На языке, подобном Java, пользователям библиотеки пришлось бы переопределять класс, чтобы добавлять методы для часто используемых шаблонов.

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

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

Просмотр утверждений с нулевой шириной

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

pattern.zeroWidthLookBehind() произвел бы (?<patten).


Вещи, которые все еще необходимо учитывать.

  • Обратные ссылки:Надеюсь, не слишком сложно с описанным ранее захватом имен
  • Как на самом деле это реализовать.Я не слишком задумывался о внутренностях.Вот тут-то и произойдет настоящее волшебство.
  • Перевод:Там действительно должен быть инструмент для перевода в классические регулярные выражения и обратно (скажем, на диалекте Perl) и новая схема.Перевод с новой схемы мог бы быть частью пакета

Собрав все это вместе, я предлагаю версию шаблона, который соответствует адресу электронной почты:

Pattern domain_label = LETTER_CHARACTER + (LETTER_CHARACTER or "-" or DIGIT_CHARACTER).anyTimes()
Pattern domain = domain_label + ("." + domain_label).anyTimes()
Pattern pattern = (LETTER_CHARACTER + ALPHANUMERIC_CHARACTER + "@" + domain).compile

Оглядываясь назад, я понимаю, что моя схема в значительной степени заимствована из используемого подхода Мартина Фаулера.Хотя я и не предполагал, что все пойдет именно так, это определенно делает использование такой системы более удобным в обслуживании.Это также решает одну или две проблемы с подходом Фаулера (захват порядка).

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

  • Он заботится о подборе скобок
  • Он обрабатывает экранирование всех "специальных" символов, что может быстро привести к аду с обратной косой чертой

Несколько простых примеров:

 // Matches a single digit
    RegExBuilder.build(anyDigit()); // "[0-9]"

 // Matches exactly 2 digits
    RegExBuilder.build(exactly(2).of(anyDigit())); // "[0-9]{2}"

 // Matches between 2 and 4 letters
    RegExBuilder.build(between(2,4).of(anyLetter())); // "[a-zA-Z]{2,4}"

И более сложный (который более или менее проверяет адреса электронной почты):

final Token ALPHA_NUM = anyOneOf(range('A','Z'), range('a','z'), range('0','9'));
final Token ALPHA_NUM_HYPEN_UNDERSCORE = anyOneOf(characters('_','-'), range('A','Z'), range('a','z'), range('0','9'));

String regexText = RegExBuilder.build(
 // Before the '@' symbol we can have letters, numbers, underscores and hyphens anywhere
    oneOrMore().of(
        ALPHA_NUM_HYPEN_UNDERSCORE
    ),
    zeroOrMore().of(
        text("."), // Periods are also allowed in the name, but not as the initial character
        oneOrMore().of(
            ALPHA_NUM_HYPEN_UNDERSCORE
        )
    ),
    text("@"),
 // Everything else is the domain name - only letters, numbers and periods here
    oneOrMore().of( 
        ALPHA_NUM
    ),
    zeroOrMore().of(
        text("."), // Periods must not be the first character in the domain
        oneOrMore().of(
            ALPHA_NUM
        )
    ),
    text("."), // At least one period is required
    atLeast(2).of( // Period must be followed by at least 2 letters (this is the TLD)
        anyLetter()
    )
);

Краткий ответ:Я видел, как к нему подходили с точки зрения компоновки и компиляции, и я думаю, что для этого стоит кое-что рассмотреть.

Длинный ответ:Компания, в которой я работаю, производит аппаратные механизмы регулярных выражений для корпоративных приложений фильтрации контента.Подумайте о запуске антивирусных или брандмауэрных приложений со скоростью 20 Гбит / с прямо на сетевых маршрутизаторах, вместо того чтобы отнимать ценные серверные или процессорные циклы.Большинство антивирусных, антиспамовых или брандмауэрных приложений по своей сути представляют собой набор регулярных выражений.

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

Возвращаясь к вопросу OP, вместо того, чтобы определять совершенно новый синтаксис, я бы написал линтер (извините, наш проприетарный) для вырезания и вставки регулярных выражений, который сломает устаревшие регулярные выражения и выведет "беглый английский", чтобы кто-то мог лучше понять.Я бы также добавил относительные проверки производительности и предложения по общим модификациям.

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

Честно говоря, кажется, что любой свободный интерфейс будет сложнее для чтения, чем стандартное регулярное выражение.Для действительно коротких выражений беглая версия является подробной, но не слишком длинной;это читабельно.Но то же самое относится и к регулярному выражению для чего-то столь длинного.

Для регулярного выражения среднего размера свободный интерфейс становится громоздким;достаточно длинный, чтобы его было трудно, если не невозможно, прочесть.

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

Знаете ли вы какой-нибудь подобный подход к регулярным выражениям?

Нет, за исключением предыдущего ответа

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

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

Как бы вы спроектировали API?

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

Я думаю, что большая часть ценности будет заключаться в определении надежной библиотеки служебных функций, таких как сопоставление "электронных писем", "телефонных номеров", "строк, которые не содержат X" и т.д.это можно настроить по своему усмотрению.

Использовали бы вы такую удобную утилиту в своих проектах?

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

Как вы думаете, было бы интересно реализовать это?;)

Конечно!

В ответ на последнюю часть вопроса (для престижа)

private static final String pattern = "(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:"
    + "\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:("
    + "?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ "
    + "\\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\0"
    + "31]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\"
    + "](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+"
    + "(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:"
    + "(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)"
    + "?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\"
    + "r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?["
    + " \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)"
    + "?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t]"
    + ")*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?["
    + " \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*"
    + ")(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)"
    + "*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+"
    + "|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r"
    + "\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:"
    + "\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t"
    + "]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031"
    + "]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\]("
    + "?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?"
    + ":(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?"
    + ":\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?"
    + ":(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?"
    + "[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*:(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|"
    + "\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>"
    + "@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\""
    + "(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?"
    + ":[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\["
    + "\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-"
    + "\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|("
    + "?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;"
    + ":\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[(["
    + "^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\""
    + ".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\"
    + "]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\"
    + "[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\"
    + "r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]"
    + "|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\0"
    + "00-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\"
    + ".|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,"
    + ";:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?"
    + ":[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:["
    + "^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]"
    + "]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)(?:,\\s*("
    + "?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:("
    + "?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=["
    + "\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t"
    + "])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t"
    + "])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?"
    + ":\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|"
    + "\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:"
    + "[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\"
    + "]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)"
    + "?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\""
    + "()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)"
    + "?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>"
    + "@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?["
    + " \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,"
    + ";:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:"
    + "\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\["
    + "\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])"
    + "*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])"
    + "+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\"
    + ".(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:("
    + "?:\\r\\n)?[ \\t])*))*)?;\\s*)";

соответствует адресам электронной почты, соответствующим RFC:D

Регулярное выражение - это описание конечного автомата.Классическое текстовое представление не обязательно плохое.Он компактен, относительно однозначен и довольно хорошо усвоен.

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

Одной из возможностей было бы создать его из множества объектов-контейнеров и объединителей.

Что-то вроде следующего (перевод этого из псевдокода в выбранный язык оставлен в качестве упражнения для нетерпеливых):

domainlabel = oneormore(characterclass("a-zA-Z0-9-"))
separator = literal(".")
domain = sequence(oneormore(sequence(domainlabel, separator)), domainlabel)
localpart = oneormore(characterclassnot("@"))
emailaddress = sequence(localpart, literal("@"), domain)

Обратите внимание, что приведенное выше неверно классифицирует произвольно большое количество адресов электронной почты как допустимые, которые НЕ соответствуют грамматике, которой они обязаны следовать, поскольку эта грамматика требует большего, чем простой FSM для полного синтаксического анализа.Однако я не верю, что это неправильно классифицировало бы действительный адрес как недействительный.

Оно должно соответствовать [^@]+@([a-zA-Z0-9-]+.)+.([ a-zA-Z0-9-]+)

4.Использовали бы вы такую удобную утилиту в своих проектах?

Скорее всего, я бы этого не сделал.Я думаю, что это тот случай, когда нужно использовать правильный инструмент для работы.Здесь есть несколько отличных ответов, таких как: 1579202 от Джереми Стейна.Недавно я "получил" регулярные выражения в недавнем проекте и обнаружил, что они очень полезны такими, какие они есть, и при правильном комментировании они понятны, если вы знаете синтаксис.

Я думаю, что часть "знание синтаксиса" - это то, что отворачивает людей от регулярных выражений, для тех, кто не понимает, что они выглядят загадочными, но они являются мощным инструментом и в прикладной информатике (напримерзарабатываю на жизнь написанием программного обеспечения) Я чувствую, что умные профессионалы должны и должны быть в состоянии научиться использовать их надлежащим образом.

Как говорится, "С большой силой приходит большая ответственность". Я видел, как люди используют регулярные выражения везде и для всего, но при разумном использовании кем-то, кто потратил время на тщательное изучение синтаксиса, они невероятно полезны;на мой взгляд, добавление еще одного слоя в некотором смысле нарушило бы их цель или, как минимум, лишило бы их силы.

Это всего лишь мое собственное мнение, и я могу понять, откуда берутся люди, которые хотели бы иметь подобный фреймворк или которые избегали бы регулярных выражений, но мне трудно слышать "Регулярные выражения - это плохо" от тех, кто не потратил время на их изучение и принятие обоснованного решения.

Чтобы все были довольны (мастера регулярных выражений и сторонники fluid interface), убедитесь, что fluid interface может выводить соответствующий шаблон raw regex, а также принимать обычные регулярные выражения с использованием заводского метода и генерировать для него fluid код.

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

Недавно у меня был это та же самая идея.

Думал реализовать это сам, но потом я нашел Вербальные выражения.

Давайте сравним:Я часто работал с (N) запросами Hibernate ICriteria, которые можно считать Свободно владеет сопоставление с SQL.Я был (и остаюсь) в восторге от них, но сделали ли они SQL-запросы более разборчивыми?Нет, скорее наоборот, но появилась еще одна выгода:стало намного проще программно создавать инструкции, разбивать их на подклассы, создавать свои собственные абстракции и т.д.

Я имею в виду, что использование нового интерфейса для данного языка, если все сделано правильно, может оказаться полезным, но не стоит думать об этом слишком высоко.Во многих случаях это не станет легче для чтения (вложенные классы символов вычитания, захваты в look-behind, if-разветвление, чтобы назвать несколько продвинутых концепций, которые будет трудно свободно комбинировать).Но точно так же во многих случаях преимущества большей гибкости перевешивают дополнительные накладные расходы, связанные со сложностью синтаксиса.

Чтобы добавить к вашему списку возможных альтернативных подходов и вырвать это из контекста только Java, рассмотрите синтаксис LINQ.Вот как это могло бы выглядеть (немного надуманно) (from, where и select являются ключевыми словами в LINQ):

// for ^str(aa|bb){3}
from part in mystring
where part startswith "str"
and part hasgroups "aa" or "bb" as first    /* "aa" or "bb" in group 'first' */
and part repeats first 3                    /* repeat group 'first' 3 times */
select part + "extra"                       /* can contain complete statement block */

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

Я говорю, дерзай, я уверен, что это интересно реализовать.

Я предлагаю использовать модель запроса (аналогичную jQuery, django ORM), где каждая функция возвращает объект запроса, так что вы можете связать их вместе.

any("a").some("b").one("@").some(chars).one(".").some(chars) //a*b+@\w+\.\w+

где chars предопределен для соответствия любому символу.

or может быть достигнуто с помощью выбора:

any("a").choice("x", "z") // a(x|z)

Аргументом для каждой функции может быть строка или другой запрос.Например, в chars переменная, упомянутая выше, может быть определена как запрос:

//this one is ascii only
chars = raw("a-zA-Z0-9")

Итак, у вас может быть "необработанная" функция, которая принимает строку регулярного выражения в качестве входных данных, если использование системы fluent query кажется вам слишком громоздким.

На самом деле, эти функции могут просто возвращать необработанные регулярные выражения, и их объединение в цепочку - это просто объединение этих необработанных строк регулярных выражений.

any("a") ---> "a*"
raw("b+") ----> "b+"
one(".") ---> "\."
choice("a", "b") ----> (a|b)

Я не уверен, что замена regexp на fluent API принесет много пользы.

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

Свободный интерфейс API сделал бы любое регулярное выражение средней сложности (скажем, ~ 50 символов) еще более сложным, чем требуется, и в конечном итоге не облегчил бы чтение, хотя это может улучшить создание из регулярного выражения в IDE, благодаря завершению кода.Но обслуживание кода, как правило, требует более высоких затрат, чем разработка кода.

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

Вы упомянули пример регулярного выражения для RFC.Я на 99% уверен (все еще есть надежда на 1%;-)), что любой API не упростил бы этот пример, но, наоборот, это только усложнило бы его чтение!Это типичный пример, когда вы все равно не хотите использовать регулярное выражение!

Даже в отношении создания регулярных выражений, из-за проблемы неоднозначных шаблонов, вполне вероятно, что с fluent API вы никогда не пришли бы с правильным выражением с первого раза, а должны были бы меняться несколько раз, пока не получите то, что действительно хотите.

Не обольщайтесь, я действительно люблю плавные интерфейсы;Я разработал несколько библиотек, которые их используют, и я использую несколько сторонних библиотек, основанных на них (напримерФЕСТИВАЛЬ по тестированию Java).Но я не думаю, что они могут стать золотым молотком для решения какой-либо проблемы.

Если мы рассматриваем исключительно Java, я думаю, что основная проблема с регулярными выражениями заключается в необходимое экранирование обратной косой черты в Java строковые константы.Это один из моментов, который невероятно затрудняет создание и понимание регулярного выражения в Java.Следовательно, первым шагом к улучшению регулярного выражения Java для меня было бы изменение языка, а - ля Заводной, где строковые константы не должны экранировать обратную косую черту.

Пока это было бы моим единственным предложением по улучшению регулярного выражения в Java.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top