用于检测半结肠终止C ++的正则表达式。而循环

时间:2009-02-07 20:43:52

标签: c++ python regex parsing recursion

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

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

......但不是这样:

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

乍一看这看起来微不足道,直到你意识到开括号和右括号之间的文字可能包含其他括号,例如:

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

我正在使用python.re模块。现在我的正则表达式看起来像这样(我已经留下了我的评论,所以你可以更容易理解):

# 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)

有人可以建议改进这个正则表达式吗?对我来说太复杂了。

10 个答案:

答案 0 :(得分:108)

你可以编写一个非常简单的例程,而不使用正则表达式:

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

停止位置是{{1}}的结束括号。现在你可以检查是否有分号。

答案 1 :(得分:20)

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

如果您正在寻找这些,那么您绝对不需要一个完整的C ++语法词法分析器/解析器。如果你想练习,你可以编写一个小的递归式解析器,但即使只是匹配括号也是如此。

答案 2 :(得分:8)

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

答案 3 :(得分:2)

我甚至不会注意这些内容。

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

^\t*for.+;$

除非您将for个语句分成多行,否则这样可以正常工作?

答案 4 :(得分:2)

试试这个正则表达式

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

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

答案 5 :(得分:1)

格雷格是完全正确的。使用正则表达式无法进行这种解析。我想有可能建立一些可能适用于许多情况的可怕的怪物,但是你会遇到一些事情。

您确实需要使用更传统的解析技术。例如,编写一个递归的正确解析器来完成你需要的工作非常简单。

答案 6 :(得分:1)

我不知道正则表达式会处理这样的事情。试试这样的事情

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

答案 7 :(得分:1)

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

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

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

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

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

这是否是一个可能的情况取决于你实际上在做什么。

答案 8 :(得分:0)

正如弗兰克所说,没有正则表达式这是最好的。这是(一个丑陋的)单线:

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

匹配评论中提到的巨魔线:

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 < 10; doSomethingTo("("))

这是通过向前运行字符串直到它到达第一个打开的paren,然后向后直到它到达第一个关闭paren为止。然后它使用这两个索引来切割字符串。

答案 9 :(得分:0)

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

问题在于,您会遇到边缘情况,这会给正则表达式增加不必要的复杂性。 @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循环体的形式搜索空语句。