【问题标题】:Regular expression to detect semi-colon terminated C++ for & while loops用于检测分号终止的 C++ for 和 while 循环的正则表达式
【发布时间】:2010-10-06 04:33:23
【问题描述】:

在我的 Python 应用程序中,我需要编写一个正则表达式来匹配以分号 (;) 终止的 C++ forwhile 循环。例如,它应该匹配这个:

for (int i = 0; i < 10; i++);

...但不是这个:

for (int i = 0; i < 10; i++)

这乍一看似乎微不足道,直到您意识到左括号和右括号之间的文本可能包含其他括号,例如:

for (int i = funcA(); i < funcB(); i++);

我正在使用 python.re 模块。现在我的正则表达式看起来像这样(我把我的 cmets 留在里面,这样你就可以更容易理解了):

# match any line that begins with a "for" or "while" statement:
^\s*(for|while)\s*
\(  # match the initial opening parenthesis
    # Now make a named group 'balanced' which matches a balanced substring.
    (?P<balanced>
        # A balanced substring is either something that is not a parenthesis:
        [^()]
        | # …or a parenthesised string:
        \( # A parenthesised string begins with an opening parenthesis
            (?P=balanced)* # …followed by a sequence of balanced substrings
        \) # …and ends with a closing parenthesis
    )*  # Look for a sequence of balanced substrings
\)  # Finally, the outer closing parenthesis.
# must end with a semi-colon to match:
\s*;\s*

这对上述所有情况都非常有效,但是一旦您尝试使 for 循环的第三部分包含一个函数,它就会中断,如下所示:

for (int i = 0; i < 10; doSomethingTo(i));

我认为它会中断,因为一旦您在左括号和右括号之间放置了一些文本,“平衡”组就会匹配包含文本,因此 (?P=balanced) 部分不再起作用,因为它不会匹配(由于括号内的文字不同)。

在我的 Python 代码中,我使用了 VERBOSE 和 MULTILINE 标志,并像这样创建正则表达式:

REGEX_STR = r"""# match any line that begins with a "for" or "while" statement:
^\s*(for|while)\s*
\(  # match the initial opening parenthesis
    # Now make a named group 'balanced' which matches
    # a balanced substring.
    (?P<balanced>
        # A balanced substring is either something that is not a parenthesis:
        [^()]
        | # …or a parenthesised string:
        \( # A parenthesised string begins with an opening parenthesis
            (?P=balanced)* # …followed by a sequence of balanced substrings
        \) # …and ends with a closing parenthesis
    )*  # Look for a sequence of balanced substrings
\)  # Finally, the outer closing parenthesis.
# must end with a semi-colon to match:
\s*;\s*"""

REGEX_OBJ = re.compile(REGEX_STR, re.MULTILINE| re.VERBOSE)

谁能建议改进这个正则表达式?这对我来说太复杂了。

【问题讨论】:

    标签: c++ python regex parsing recursion


    【解决方案1】:

    您可以编写一个非常简单的小程序来执行此操作,而无需使用正则表达式:

    • 设置一个位置计数器pos,使其指向forwhile 之后的左括号之前。
    • 将开括号计数器openBr 设置为0
    • 现在继续递增pos,读取相应位置的字符,并在看到左括号时递增openBr,并在看到右括号时递减。这将在开始时增加一次,对于“for (”中的第一个左括号,对于中间的一些括号增加和减少一些,并在for 括号关闭时将其设置回0
    • 所以,当openBr 再次变为0 时停止。

    停止位置是for(...) 的右括号。现在您可以检查是否有分号。

    【讨论】:

    • 你还需要考虑cmets和strings,这两者都会抛出这个算法。
    • 您可以使用正则表达式预先删除 cmets 和字符串。 :) 或者引入更多变量,例如 openBr,指示您是否在评论中(以及评论的类型,因此您知道哪个字符关闭它)或字符串。
    • 巨魔行:for (int i = 0; i &lt; 10; doSomethingTo("("));
    • 我实现了一个类似的算法来解析 C 中的函数,但是我遇到了预处理器指令(如 #ifdef)中的括号问题。关于如何解决这个问题的任何想法?
    • 这里有一个轻量级的Javascript implementation of Frank's algorithm,如果有人感兴趣的话
    【解决方案2】:

    这是你真的不应该用正则表达式做的事情。一次只解析一个字符,跟踪开/关括号。

    如果这就是您要寻找的全部内容,那么您绝对不需要成熟的 C++ 语法词法分析器/解析器。如果你想练习,你可以写一个递归体面的解析器,但即使是匹配括号也有点多。

    【讨论】:

    • 实际上,通过 boost:xpressive 和可能的 python,您可以使用正则表达式来执行平衡的括号匹配。
    【解决方案3】:

    这是在工作中使用错误工具的一个很好的例子。正则表达式不能很好地处理任意嵌套的子匹配。你应该做的是使用真正的词法分析器和解析器(C++ 的语法应该很容易找到)并寻找意外的空循环体。

    【讨论】:

    • +1,严格来说,正则表达式根本不处理嵌套表达式。处理嵌套表达式的正则表达式已经超越了上下文无关语法。
    • 我同意使用 flex/yacc 或类似的。但是 C++ 语法真的很容易找到吗?有人有链接吗?我记得 CDT/Eclipse 的人很难正确快速地真正解析 C++ 输入。
    • 也许不是; C++ 当然是出了名的难以解析。由于原始问题不需要对输入源进行完整的语义分析,因此更简单、不完整的解析器可能也能完成这项工作。
    • 实际上,通过 boost:xpressive,您可以使用正则表达式来执行平衡的括号匹配。
    【解决方案4】:

    试试这个正则表达式

    ^\s*(for|while)\s*
    \(
    (?P<balanced>
    [^()]*
    |
    (?P=balanced)
    \)
    \s*;\s
    

    我删除了(?P=balanced) 周围的包装\( \) 并将* 移动到任何非括号序列后面。我使用 boost xpressive 完成了这项工作,并重新检查了该网站 (Xpressive) 以刷新我的记忆。

    【讨论】:

      【解决方案5】:

      我什至不会注意括号的内容。

      只需匹配以for 开头并以分号结尾的任何行:

      ^\t*for.+;$
      

      除非您将 for 语句拆分为多行,否则可以正常工作吗?

      【讨论】:

      • 这可能还不够,因为人们确实将 for() 语句拆分为多行。
      • dehmann 是正确的 - 这个想法是模式匹配来自真实代码库的示例,因此它必须能够处理所有有效的 for 循环结构,包括多行结构。
      【解决方案6】:

      我不知道正则表达式能很好地处理这样的事情。试试这样的

      line = line.Trim();
      if(line.StartsWith("for") && line.EndsWith(";")){
          //your code here
      }
      

      【讨论】:

      • +1。当然,我们在这里讨论的是 Python,所以语法略有不同。但是,如果您实际上没有正确解析 C,则没有理由在“for”行的末尾寻找除“);”之外的任何内容。
      【解决方案7】:

      聚会有点晚了,但我认为正则表达式不是工作的正确工具

      问题是您会遇到边缘情况,这会给正则表达式增加额外的复杂性。 @est提到an example line

      for (int i = 0; i < 10; doSomethingTo("("));
      

      这个字符串文字包含一个(不平衡的!)括号,这破坏了逻辑。显然,您必须忽略字符串文字的内容。为此,您必须考虑双引号。但是字符串文字本身可以包含双引号。例如,试试这个:

      for (int i = 0; i < 10; doSomethingTo("\"(\\"));
      

      如果你使用正则表达式来解决这个问题,它会给你的模式增加更多的复杂性。

      我认为你最好解析语言。例如,您可以使用像 ANTLR 这样的语言识别工具。 ANTLR 是一个解析器生成器工具,它也可以生成一个parser in Python。您必须提供定义目标语言的语法,在您的情况下为 C++。那里已经有许多语言的语法,所以你可以抓住the C++ grammar

      然后您可以轻松遍历解析器树,搜索空语句为 whilefor 循环体。

      【讨论】:

        【解决方案8】:

        格雷格是绝对正确的。这种解析不能用正则表达式来完成。我想有可能构建一些适用于许多情况的可怕的怪物,但是你只会遇到一些有用的东西。

        您确实需要使用更传统的解析技术。例如,编写一个像样的递归解析器来做你需要的事情非常简单。

        【讨论】:

          【解决方案9】:

          另一个忽略括号并将for 视为包含三个分号分隔值的构造的想法:

          for\s*\([^;]+;[^;]+;[^;]+\)\s*;
          

          即使拆分为多行(启用 MULTILINE 后),此选项也有效,但假定 for ( ... ; ... ; ... ) 是唯一有效的构造,因此不适用于 for ( x in y ) 构造或其他偏差。

          还假设没有包含分号作为参数的函数,例如:

          for ( var i = 0; i < ListLen('a;b;c',';') ; i++ );
          

          这是否可能取决于您实际执行此操作的目的。

          【讨论】:

            【解决方案10】:

            正如弗兰克建议的那样,最好不要使用正则表达式。这是(丑陋的)单线:

            match_string = orig_string[orig_string.index("("):len(orig_string)-orig_string[::-1].index(")")]
            

            匹配他评论中提到的 troll line est:

            orig_string = "for (int i = 0; i < 10; doSomethingTo(\"(\"));"
            match_string = orig_string[orig_string.index("("):len(orig_string)-orig_string[::-1].index(")")]
            

            返回(int i = 0; i &lt; 10; doSomethingTo("("))

            这是通过向前运行字符串直到它到达第一个打开的括号,然后向后运行直到它到达第一个关闭的括号。然后它使用这两个索引对字符串进行切片。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-04-12
              • 1970-01-01
              • 2013-11-10
              • 2013-03-25
              • 1970-01-01
              • 2011-07-04
              • 1970-01-01
              • 2016-02-14
              相关资源
              最近更新 更多