Подтвердить что ты не робот

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

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

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

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

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

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

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

  • Как вы проектируете API?

  • Вы использовали бы такую ​​опрятную утилиту в своих проектах?

  • Считаете ли вы, что это будет весело?;)

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

// matches [email protected] - think of it as reusable expressions
Pattern p = string{"a").anyTimes().string("[email protected]").domain().compile();

EDIT - краткое изложение комментариев:

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

OPEN BOUNTY

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

РЕДАКТИРОВАТЬ - ПРИМЕРНЫЙ ПРИМЕР:

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

Ответ 1

Как вы создадите API?

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

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

Используйте шаблон, например:

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

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

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

Не офсайд, нет.

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

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

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

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

Считаете ли вы, что это будет весело?;)

+ 1

Ответ 2

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

 "^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

Ответ 3

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

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

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

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}»

Ответ 4

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

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

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

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

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

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

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

3. Как вы проектируете API?

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

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

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

5. Считаете ли вы, что это будет весело?;)

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


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

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

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

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

Изменить: Кажется, я использовал следующие условные обозначения. Поэтому я разъяснил их и переместил их сюда.

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

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

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

Как обрабатывается захват/кластеризация?

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

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

Я ожидаю, что захват будет выполнен как метод экземпляра на шаблоне. Взятие переменной для хранения соответствующей строки [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/

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

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

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

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

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

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

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

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

Нулевая ширина выглядит вокруг утверждений

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

pattern.zeroWidthLookBehind() произведет (?<patten).


Вещи, которые еще нужно учитывать.

  • Backreferences: Надеюсь, что не так сложно с названным захватом, обсуждавшимся ранее
  • Как реально реализовать его. Я не слишком много думал о внутренностях. Там, где будет происходить настоящая магия.
  • Перевод: на самом деле должен быть инструмент для перевода и из классических регулярных выражений (например, диалект на 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

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

Ответ 5

Моя собственная скромная попытка можно найти на 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()
    )
);

Ответ 7

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

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

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

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

Ответ 8

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

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

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

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

Ответ 9

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

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

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

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

Как вы создадите API?

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

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

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

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

Считаете ли вы, что это будет весело?;)

Конечно!

Ответ 10

В ответ на последнюю часть вопроса (для 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

Ответ 11

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

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

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

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

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 -] +)

Ответ 12

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

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

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

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

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

Ответ 13

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

Ответ 16

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

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

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

Ответ 17

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

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

any("a").some("b").one("@").some(chars).one(".").some(chars) //a*[email protected]\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)

Ответ 18

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

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

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

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

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

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

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

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

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