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

Я только что видел огромное регулярное выражение для 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 лет, не так ли?

OPEN BOUNTY

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

РЕДАКТИРОВАТЬ - хороший пример:

вот такой шаблон, о котором я говорю, - дополнительные похвалы первому, кто сможет его перевести - RegexBuddies разрешил (кстати, от проекта Apache) дополнительные награды Chii и Chii: это шаблон проверки адреса электронной почты, совместимый с 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*)";

19 ответов

Решение

Как бы вы разработали 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, может взять ваше регулярное выражение и перевести его в:

Подберите либо регулярное выражение, приведенное ниже (попытка использовать следующую альтернативу, только если она не удалась) "a *"
   Совпадение символа "а" буквально "а *"
      От нуля до неограниченного количества раз, столько раз, сколько возможно, отдача по мере необходимости (жадный) "*"
Или сопоставьте регулярное выражение с номером 2 ниже (если попытка сопоставления не удалась, вся попытка сопоставления не удалась) "b{2,5}"
   Совпадение символа "b" буквально "b{2,5}"
      От 2 до 5 раз, столько раз, сколько возможно, отдача по мере необходимости (жадный) "{2,5}"

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

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

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

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

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

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

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

3. Как бы вы разработали API?

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

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

Еще не решил. У меня нет проблем с текущей версией загадочных регулярных выражений. И мне нужен инструмент для преобразования существующих регулярных выражений в версию на свободном языке.

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 // 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, вместо того, чтобы определять совершенно новый синтаксис, я бы написал linter (извините, наш проприетарный) вырезал и вставлял регулярные выражения, которые будут разбивать устаревшие регулярные выражения и выводить "свободный английский", чтобы кто-то мог лучше понять. Я также добавил бы относительные проверки производительности и предложения для общих модификаций.

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

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

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

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

У меня недавно была такая же идея.

Думал реализовать это сам, но потом нашел https://g ithub.com/VerbalExpressions.

То, что вы ищете, можно найти здесь:. Это регулярное выражение, которое следует за Wizard Design Pattern

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

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

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

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

domainlabel = oneormore (символьный класс ("a-zA-Z0-9-")) разделитель = литерал (".")
domain = sequence(oneormore(последовательность (domainlabel, разделитель)), domainlabel)
localpart = oneormore(символьный класс not("@")) адрес электронной почты = последовательность (локальная часть, литерал ("@"), домен)

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

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

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

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

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

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

Нет, кроме предыдущего ответа

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

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

Как бы вы разработали API?

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

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

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

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

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

Конечно!

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

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

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

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

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

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

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

Пример из его readme:

      BeeFragment myFragment = Bee
        .then(Bee.BEGIN)
        .then(Bee.ASCII_WORD)
        .then(Bee.WHITESPACE.any())
        .then(Bee.ASCII_WORD.as("nameX"))
        .then(Bee.intBetween(-4, 1359)
                .or(Bee.fixed("-").more(Greediness.POSSESSIVE)))
        .then(Bee.ref("nameX"))
        .then(Bee.fixed("??)fixed?text. ").optional())
        .then(Bee.ASCII_WORD.optional(Greediness.LAZY))
        .then(Bee.END);

Pattern myPattern = myFragment.toPattern();
Matcher myMatcher = myPattern.matcher(someString);

if (myMatcher.matches()) {
    String nameX = myMatcher.group("nameX");
    System.out.println(String.format("We have a nice day, %s!", nameX));
}

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

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

Чтобы добавить в свой список возможных альтернативных подходов и вывести это из контекста только 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.

Я не уверен, что замена регулярного выражения на свободный API принесет много.

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

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

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

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

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

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

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

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

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

Я предлагаю использовать модель запросов (аналогично 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")

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

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

any("a") ---> "a*"
raw("b+") ----> "b+"
one(".") ---> "\."
choice("a", "b") ----> (a|b)
Другие вопросы по тегам