在我的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)
有人可以建议改进这个正则表达式吗?对我来说太复杂了。
答案 0 :(得分:108)
你可以编写一个非常简单的例程,而不使用正则表达式:
pos
,使其指向for
或while
之后的左括号前面。 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。
然后,您可以轻松地遍历解析器树,以while
或for
循环体的形式搜索空语句。