Существует ли регулярное выражение для обнаружения правильного регулярного выражения?

589

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

  • 1
    Вышеупомянутое может или не может быть полезным на практике (я не рассмотрел их подробно, и при этом я не являюсь особенно хорошим судьей), но формально, языки регулярных выражений IIRC (по крайней мере некоторые) являются законченными по Тьюрингу, и как таковые это невозможно построить тестер, который будет правильно оценивать правильность всех возможных регулярных выражений на этих языках. См. Теорему Гёделя о неполноте и тезис Черча-Тьюринга.
  • 327
    Кто проверяет правильность регулярного выражения?
Показать ещё 18 комментариев
Теги:

8 ответов

593
Лучший ответ
/
^                                             # start of string
(                                             # first group start
  (?:
    (?:[^?+*{}()[\]\\|]+                      # literals and ^, $
     | \\.                                    # escaped characters
     | \[ (?: \^?\\. | \^[^\\] | [^\\^] )     # character classes
          (?: [^\]\\]+ | \\. )* \]
     | \( (?:\?[:=!]|\?<[=!]|\?>)? (?1)?? \)  # parenthesis, with recursive content
     | \(\? (?:R|[+-]?\d+) \)                 # recursive matching
     )
    (?: (?:[?+*]|\{\d+(?:,\d*)?\}) [?+]? )?   # quantifiers
  | \|                                        # alternative
  )*                                          # repeat content
)                                             # end first group
$                                             # end of string
/

Это рекурсивное регулярное выражение и не поддерживается многими механизмами регулярных выражений. Основанные на PCRE должны поддерживать его.

Без пробелов и комментариев:

/^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d+(?:,\d*)?\})[?+]?)?|\|)*)$/

.NET не поддерживает рекурсию напрямую. (Конструкции (?1) и (?R).) Рекурсия должна быть преобразована в подсчет сбалансированных групп:

^                                         # start of string
(?:
  (?: [^?+*{}()[\]\\|]+                   # literals and ^, $
   | \\.                                  # escaped characters
   | \[ (?: \^?\\. | \^[^\\] | [^\\^] )   # character classes
        (?: [^\]\\]+ | \\. )* \]
   | \( (?:\?[:=!]
         | \?<[=!]
         | \?>
         | \?<[^\W\d]\w*>
         | \?'[^\W\d]\w*'
         )?                               # opening of group
     (?<N>)                               #   increment counter
   | \)                                   # closing of group
     (?<-N>)                              #   decrement counter
   )
  (?: (?:[?+*]|\{\d+(?:,\d*)?\}) [?+]? )? # quantifiers
| \|                                      # alternative
)*                                        # repeat content
$                                         # end of string
(?(N)(?!))                                # fail if counter is non-zero.

Уплотненный:

^(?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>|\?<[^\W\d]\w*>|\?'[^\W\d]\w*')?(?<N>)|\)(?<-N>))(?:(?:[?+*]|\{\d+(?:,\d*)?\})[?+]?)?|\|)*$(?(N)(?!))
  • 0
    Будет ли это проверять замены и переводы?
  • 0
    Он будет проверять только часть регулярных выражений с заменами и переводами. s / <эта часть> /.../
Показать ещё 19 комментариев
227

Вряд ли.

Оцените его в try..catch или независимо от того, что предоставляет ваш язык.

  • 148
    Это не очень предприимчиво с вашей стороны.
  • 112
    Я думаю, что это гораздо лучшее решение, чем пытаться проверить его с помощью регулярных выражений ....
Показать ещё 6 комментариев
154

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

Существует одно ограничение регулярных выражений, которое делает невозможным запись регулярного выражения, которое соответствует всем и только регулярным выражениям. Вы не можете сопоставлять реализации, например, фигурные скобки. Regexes использует много таких конструкций, поэтому возьмем [] в качестве примера. Всякий раз, когда есть [должно быть соответствие]. Достаточно просто для регулярного выражения "[. *]".

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

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

EDIT: Закончилось писать сообщение в блоге об этом: Ограничения регулярного выражения

  • 2
    Мне часто приходится различать общий инструмент сопоставления текста, называемый регулярным выражением, и регулярное выражение, на котором он основан. К сожалению, многие не видят различия. RE2 уникален тем, что допускает только расширение, которое может быть переведено обратно в обычный RE. Он также обладает всеми преимуществами RE (ограниченная память, время выполнения, скорость), с большинством синтаксических расширений.
  • 0
    Почему регулярное выражение не может найти пары скобок? Я написал синтаксический анализатор моего собственного языка, и он может проверить, имеет ли каждая скобка подходящее окончание. Проверьте это: regex101.com/r/y4xhYo/1
Показать ещё 2 комментария
37
Хороший вопрос. Истинные регулярные языки не могут решить произвольно глубоко вложенные хорошо сформированные круглые скобки. То есть, если ваш алфавит содержит '(' и ')', цель состоит в том, чтобы решить, имеет ли строка из них хорошо сформированные соответствующие скобки. Так как это необходимое требование для регулярных выражений, то ответ - нет.

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

Russ Cox написал замечательный трактат о реализации двигателя regex: Регуляция регулярных выражений может быть простой и быстрой

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

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

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

SKIP :
{
    " "
|   "\r"
|   "\t"
|   "\n"
}
TOKEN : 
{
    < DIGITO: ["0" - "9"] >
|   < MAYUSCULA: ["A" - "Z"] >
|   < MINUSCULA: ["a" - "z"] >
|   < LAMBDA: "LAMBDA" >
|   < VACIO: "VACIO" >
}

IRegularExpression Expression() :
{
    IRegularExpression r; 
}
{
    r=Alternation() { return r; }
}

// Matchea disyunciones: ER | ER
IRegularExpression Alternation() :
{
    IRegularExpression r1 = null, r2 = null; 
}
{
    r1=Concatenation() ( "|" r2=Alternation() )?
    { 
        if (r2 == null) {
            return r1;
        } else {
            return createAlternation(r1,r2);
        } 
    }
}

// Matchea concatenaciones: ER.ER
IRegularExpression Concatenation() :
{
    IRegularExpression r1 = null, r2 = null; 
}
{
    r1=Repetition() ( "." r2=Repetition() { r1 = createConcatenation(r1,r2); } )*
    { return r1; }
}

// Matchea repeticiones: ER*
IRegularExpression Repetition() :
{
    IRegularExpression r; 
}
{
    r=Atom() ( "*" { r = createRepetition(r); } )*
    { return r; }
}

// Matchea regex atomicas: (ER), Terminal, Vacio, Lambda
IRegularExpression Atom() :
{
    String t;
    IRegularExpression r;
}
{
    ( "(" r=Expression() ")" {return r;}) 
    | t=Terminal() { return createTerminal(t); }
    | <LAMBDA> { return createLambda(); }
    | <VACIO> { return createEmpty(); }
}

// Matchea un terminal (digito o minuscula) y devuelve su valor
String Terminal() :
{
    Token t;
}
{
    ( t=<DIGITO> | t=<MINUSCULA> ) { return t.image; }
}
  • 5
    Будучи немного лучше, я согласен, что вы должны придерживаться одного языка. И, не говоря про-английский или «ваш язык отстой», Линус Торвальдс, по крайней мере, уже предлагает стандарт.
  • 20
    Я согласен, что использование испанского, английского и Spanglish в одном и том же коде не является удачной практикой. Проблема в том, что я привык писать по-английски, но в проекте были некоторые рекомендации (например, комментирование на испанском или использование определенных имен для токенов). В любом случае, цель состояла в том, чтобы просто дать представление об алгоритме, а не дать полный справочный код.
Показать ещё 2 комментария
5

Вы можете отправить регулярное выражение в preg_match, которое вернет false, если регулярное выражение недействительно. Не забудьте использовать "@" для подавления сообщений об ошибках:

@preg_match($regexToTest, '');
  • вернет 1, если регулярное выражение равно //.
  • вернет 0, если регулярное выражение в порядке.
  • в противном случае возвращает false.
4

Этот пример в вики-каталоге pyparsing дает грамматику для разбора некоторых регулярных выражений с целью возврата набора совпадающих строк. Таким образом, он отвергает те, которые включают неограниченные термины повторения, такие как "+" и "*". Но это должно дать вам представление о том, как структурировать парсер, который будет обрабатывать re.

-2

Попробуйте этот...

//regular expression for email
    var pattern = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    if(pattern.test(email)){
        return true;
    } else {
        return false;
    }

Ещё вопросы

Сообщество Overcoder
Наверх
Меню