【问题标题】:How can I normalize/canonize a regular expression pattern?如何规范化/规范化正则表达式模式?
【发布时间】:2011-03-08 14:45:23
【问题描述】:

我有一个用代码构建的复杂正则表达式。我想将它规范化为最简单(规范)的形式,这将是一个等效的正则表达式,但没有额外的括号等等。

我希望它被规范化,这样我就可以了解它是否正确并找出其中的错误。

这是我要规范化的正则表达式的示例:

^(?:(?:(?:\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]))*)))))*))))*))$

【问题讨论】:

  • 你就不能自己硬着头皮做吗?
  • 即使没有不必要的括号,它仍然是一堆难以理解的字符。我建议您将其视为一个黑匣子,并对其进行大量单元测试。

标签: c# .net regex normalization


【解决方案1】:

证明正确性并不是进行归一化的好动机,因为范式可能非常模糊且完全无法识别。

要获得正确性,您可以 1) 对其进行大量测试 2) 获取状态机并通过归纳证明正确性。

【讨论】:

    【解决方案2】:

    我认为你已经超越了自己;该正则表达式的问题不仅仅是装饰性的。许多括号可以简单地删除,如(?:[ \t]+),但我怀疑其中一些正在以您不希望的方式改变正则表达式的含义。

    例如,(?:|[^\x00-\x31\x127\"]) 应该是什么意思?在开头使用该管道,它相当于[^\x00-\x31\x127\"]??--零或一个,不情愿地,任何字符类匹配。这真的是你想要的吗?

    角色类本身也很可疑。它显然是为了匹配除 ASCII 控制字符或引号之外的任何内容,但数字是十进制的,它们应该是十六进制:[^\x00-\x1E\x7F\"]

    【讨论】:

      【解决方案3】:

      到目前为止,我同意其他答案和 cmets。即使你可以定义一个简化形式,也不太可能比这个更容易理解,它类似于 1200 波特调制解调器上的线路噪声。

      如果您确实想找到正则表达式的规范形式,我将首先准确定义“规范形式”的含义。例如,假设您有正则表达式[ABCDEF-I]。是规范形式 (1) [ABCDEF-I], (2) [ABCDEFGHI] 还是 (3) [A-I]

      也就是说,出于规范化的目的,您是否希望 (1) 出于规范化的目的忽略正则表达式的这个子集,(2) 消除所有“-”运算符,从而简化表达式,或者 (3) 使更短吗?

      最简单的方法是遍历正则表达式规范的每个部分,找出哪些子表达式在逻辑上等价于另一种形式,然后确定两者中的哪一个“更规范”。然后编写一个递归正则表达式分析器,该分析器通过正则表达式并将每个子表达式替换为其规范形式。在循环中继续这样做,直到找到“固定点”,即当您将其置于规范形式时不会改变的正则表达式。

      但是,这不一定会满足您的需求。如果您想要重新组织正则表达式以最小化分组的复杂性或类似的事情,那么您可能想要做的是规范化正则表达式,使其具有仅具有分组、联合和 Kleene 的形式明星运营商。一旦采用这种形式,您就可以轻松地将其转换为确定性有限自动机,一旦采用 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 +
      

      ...

      一旦看起来像这样,它就会更容易理解和优化。

      【讨论】:

      • 这是我很久以来看到的最酷的东西了......然后我看到了它是谁发布的。埃里克,你的洞察力总是让我惊讶。希望我能为这个投一百万票。
      • 有些库以更易读的方式做到这一点,例如:flimflan.com/blog/ReadableRegularExpressions.aspx
      • noq 是什么意思?为什么beginningend 在它们的字符串前面有@ 符号,而newlinetabbackslash 没有利用它?而且我认为bar 读起来更适合or
      • Gabe:没有问号的组太长。因为当我意识到我不需要 @ 符号时,我打字很快,也没有费心去编辑它们。没有特别的原因。当然。请记住,这只是一个想法的草图;我的目的不是为正则表达式开发一个成熟的流畅模型。
      • 好的,那么请允许我稍微改进一下。我不想更改其他人的代码,如果他们有充分的理由说明它是这样的。
      【解决方案4】:

      我只是把它写成一个扩展的形式:

      ^
      (?:
        (?: (?: \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])
                                 )*
                              )
                            )
                        )
                      )
                    )*
                  )
                )
            )
          )
        )
      )
      $
      

      您可以快速定位不必要的分组,并定位一些错误。我看到的一些错误:

      • 缺少指定组的?。应该是(?&lt;name&gt; )
      • 没有结束双引号 (")。

      您甚至可以以这种形式使用正则表达式。如果在构造 Regex 对象时提供标志 RegexOptions.IgnorePatternWhitespace,则模式中的任何空格或 cmets (#) 都将被忽略。

      【讨论】:

      • 重命名组:如果你在谈论&lt;transfer-coding&gt;,那无论如何这不是一个有效的组名。 .NET 组名只能包含字母和数字。
      【解决方案5】:

      我不知道有任何工具可以做到这一点。我什至强烈怀疑正则表达式是否存在诸如规范形式之类的东西——它们足够复杂,通常有几种截然不同的解决方案。

      如果这个表达式是生成器的输出,那么对我来说(单元)测试代码生成器似乎更有希望。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-14
      • 2011-03-21
      • 2019-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-06
      相关资源
      最近更新 更多