找到与Regex匹配的括号

时间:2010-08-29 10:14:15

标签: .net regex parsing grammar matching

输入是表示元素列表的字符串。

列表定义为开放式卷曲{,后跟0个或更多元素,用空格分隔后跟闭合卷曲}

元素是文字或元素列表。

文字是一系列非空白字符。如果元素包含大括号,则必须使用反斜杠对其进行转义:\{\}。 (或者你可以假设文字中不允许使用curlies,为简单起见)

示例:

"{abc { def ghi } 7 { 1 {2} {3 4} } {5 6} x\{yz \}foo }"

文字中没有curlies:

"{abc { def ghi } 7 { 1 {2} {3 4} } {5 6} xyz foo }"

(这是Tcl列表的简化定义。)

我想知道的是:可以使用正则表达式将输入拆分为最外层循环的元素吗?

预期产出:

abc
{ def ghi }
7
{ 1 {2} {3 4} }
{5 6}
x{yz
}foo

真正的问题是:这可以用正则表达式完成吗?

我对.NET风格最感兴趣,但会接受任何答案。

我会在答案中发布自己的假设,看看它是否经过验证或销毁。

4 个答案:

答案 0 :(得分:4)

不幸的是,对于Regex的某些风格,答案是肯定的,例如PCRE和.NET因为它们分别支持递归模式和类似堆栈的操作。

语法可以写成

ELEMENT  -> (?!\{)\S+ | LIST
LIST     -> '\{\s*' ELEMENT? ('\s+' ELEMENT)* '\s*\}' 

因此在PCRE中,这可以转换为模式:

   \{\s*(?0)?(?:\s+(?0))*\s*\}|(?!\{)(?:[^\s}]|\}(?![\s}]))+

#  ---------------------------                   ^^^^^^^^^
#            LIST                    Make sure the } is not closing the group

例如,请参阅http://www.ideone.com/SnGsU(为简单起见,我已删除了顶级{}

(当然,不要在工作中尝试这个:))

(顺便说一句,我不知道如何将这个PCRE转换成.NET风格。如果有人知道,请尝试Converting PCRE recursive regex pattern to .NET balancing groups definition

答案 1 :(得分:3)

好吧,编辑从标记中移除花括号并从问题中获取刺痛,现在使用平衡组很容易使用.Net Regexes。它只是匹配括号,这是一个基本的例子 就像KennyTM的回答一样,这只有在你移除顶级支架时才有效,或者它会与整个输入相匹配。
同样,这更适合用于娱乐目的:

(?:                    # try matching...
    (?:\\[{}]|[^\s{}])+\s*? # a literal (allow escaped curly braces)
    |                       # OR
    (?<Curly>{)\s*          # "{" and push to stack
    |                       # OR
    (?<-Curly>})\s*?        # "}", pop from stack and fail if the stack is empty
)+?                    # ...a few times, and stop whenever you can.
(?(Curly)(?!))         # Make sure there aren't any extra open curly braces

有关详细信息,请参阅此文章:Regex Balancing Group in Depth

答案 2 :(得分:2)

对此的传统答案是响亮的“不”。正如我们在编译器类中学到的那样,常规语法不能用于描述具有递归定义的语言(即,您不能使用有限状态机)

这里需要的是一个无上下文解析器,其实现归结为有限状态机+一个堆栈。
请参阅ANTLRbison等。

答案 3 :(得分:1)

@Cristi关于正则表达式是正确的:理论上不可能使用无堆栈的有限状态自动机来解决递归表达式。 但是,解决方案更简单:您只需要保持打开括号数量的计数器,并确保它不会低于0.它比维护堆栈节省更多内存,而您只需要计数 - 而不是圆括号的内容。

算法:

counter = 0                        // Number of open parens
For char c in string:              
    print c                        
    if c=='{':                     // Keep track on number of open parens
        counter++
    if c=='}':
        counter--
    if counter==1:                 // New line if we're back to the top level
        print "\n"
    elif counter<1:                // Error if the nesting is malformed
        print "ERROR: parentheses mismatch"
        break