Как я могу нормализовать/канонизировать образец регулярного выражения?

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

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

Вот пример для регулярного выражения, которое я хочу нормализовать:

^(?:(?:(?:\r\n(?:[ \t]+))*)(<transfer-coding>(?:chunked|(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]+)(?:(?:;(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]+)=(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]+)|(?:"(?:(?:(?:|[^\x00-\x31\x127\"])|(?:\\[\x00-\x127]))*)))))*))))(?:(?:(?:\r\n(?:[ \t]+))*),(?:(?:\r\n(?:[ \t]+))*)(<transfer-coding>(?:chunked|(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]+)(?:(?:;(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]+)=(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]+)|(?:"(?:(?:(?:|[^\x00-\x31\x127\"])|(?:\\[\x00-\x127]))*)))))*))))*))$
5
задан Lance Roberts 18 February 2013 в 23:40
поделиться

4 ответа

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

Если вы действительно хотите найти каноническую форму для регулярных выражений, я бы начал с точного определения того, что вы подразумеваете под "канонической формой". Например, предположим, у вас есть регулярное выражение [ABCDEF-I]. Является ли канонической формой (1) [ABCDEF-I], (2) [ABCDEFGHI] или (3) [A-I]?

То есть, для целей канонизации вы хотите (1) игнорировать это подмножество регулярных выражений, (2) убрать все операторы "-", тем самым упростив выражение, или (3) сделать его короче?

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

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

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

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

Func<string, string> group = s=>"(?:"+s+")";
Func<string, string> capture = s=>"("+s+")";
Func<string, string> anynumberof = s=>s+"*";
Func<string, string> oneormoreof = s=>s+"+";
var beginning = "^";
var end = "$";
var newline = @"\r\n";
var tab = @"\t";
var space = " ";
var semi = ";";
var comma = ",";
var equal = "=";
var chunked = "chunked";
var transfer = "<transfer-coding>";
var backslash = @"\\";
var escape = group(backslash + @"[\x00-\x7f]");
var or = "|";
var whitespace = 
    group(
        anynumberof(
            group(
                newline +  
                group(
                    oneormoreof(@"[ \t]")))));
var legalchars = 
    group(
        oneormoreof(@"[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]"));

var re = 
    beginning + 
    group(
        whitespace + 
        capture(
            transfer + 
            group(
                chunked + 
                or + 
                group(
                    legalchars + 
                    group(
                        group(
                            semi + 
                            anynumberof(
                                group(
                                    legalchars + 
                                    equal +

...

Как только оно примет такой вид, его будет гораздо легче понять и оптимизировать.

12
ответ дан 18 December 2019 в 09:05
поделиться

Думаю, вы забегаете вперед; проблемы с этим регулярным выражением не только косметические. Многие скобки можно просто опустить, как в (?: [\ T] +) , но я подозреваю, что некоторые из них изменяют значение регулярного выражения не так, как вы намеревались.

Например, что должно означать (?: | [^ \ x00- \ x31 \ x127 \ "]) ? С этой вертикальной чертой в начале это эквивалентно [^ \ x00- \ x31 \ x127 \ "] ?? - ноль или один, неохотно, независимо от того, что соответствует классу символов. Это действительно то, что вы намеревались сделать?

Сам класс персонажа также вызывает большие подозрения. Очевидно, это означает соответствие чему-либо, кроме управляющего символа ASCII или кавычек, но числа являются десятичными, а должны быть шестнадцатеричными: [^ \ x00- \ x1E \ x7F \ "]

3
ответ дан 18 December 2019 в 09:05
поделиться

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

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

2
ответ дан 18 December 2019 в 09:05
поделиться

Я бы просто написал это в развернутом виде:

^
(?:
  (?: (?: \r\n (?:[ \t]+) )* )
  (<transfer-coding>
    (?: chunked
     |  (?:
          (?:[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]+)
          (?:
            (?:
              ;
              (?:
                (?:[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]+)
                =
                (?: (?:[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]+)
                 |  (?:
                      "
                      (?:
                        (?:
                          (?:
                           |  [^\x00-\x31\x127\"]
                           )
                         | (?:\\[\x00-\x127])
                         )*
                      )
                    )
                )
              )
            )*
          )
        )
    )
  )
  (?:
    (?: (?: \r\n (?:[ \t]+) )* )
    ,
    (?: (?: \r\n (?:[ \t]+) )* )
    (<transfer-coding>
      (?: chunked
       |  (?:
            (?:[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]+)
            (?:
              (?:
                ;
                (?:
                  (?:[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]+)
                  =
                  (?: (?:[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E\x7A\x7C\x7E-\xFE]+)
                   |  (?:
                        "
                        (?:
                          (?:
                            (?:
                             |  [^\x00-\x31\x127\"]
                             )
                           | (?:\\[\x00-\x127])
                           )*
                        )
                      )
                  )
                )
              )*
            )
          )
      )
    )
  )
)
$

Вы можете быстро найти ненужную группировку и найти некоторые ошибки. Некоторые ошибки, которые я видел:

  • Отсутствует ? для названных групп. Это должно быть (? <Имя>) .
  • Запрещается закрывающая двойная кавычка ( ").

Вы даже можете использовать регулярное выражение в этой форме. Если вы укажете флаг RegexOptions.IgnorePatternWhitespace при построении Regex , любые пробелы или комментарии ( # ) в шаблоне будут проигнорированы.

1
ответ дан 18 December 2019 в 09:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: