理论上的正则表达式不足以进行括号匹配。理论上的正则表达式只能处理左递归/右递归规则。中间递归规则不能用正则表达式表示(例如<exp> -> "(" <exp> ")"
)。
然而,编程语言中的正则表达式实现了允许正则表达式超越常规语法的功能。例如,正则表达式中的反向引用允许编写匹配非上下文无关语言的正则表达式。但是,即使使用反向引用,仍然无法用正则表达式平衡括号。
由于 PCRE 库通过子程序调用功能支持递归正则表达式,因此在技术上可以使用正则表达式解析这样的表达式。但是,除非您可以自己编写正则表达式,这意味着您了解自己在做什么并且可以修改正则表达式以满足您的需要,否则您应该编写自己的解析器。否则,您最终将陷入无法维护的混乱局面。
(?(DEFINE)
(?<string>'[^']++')
(?<int>\b\d+\b)
(?<sp>\s*)
(?<key>\b\w+\b)
(?<value>(?&string)|(?&int))
(?<exp>(?&key) (?&sp) = (?&sp) (?&value))
(?<logic>\b (?:and|or) \b)
(?<main>
(?<token> \( (?&sp) (?&main) (?&sp) \) | (?&exp) )
(?:
(?&sp) (?&logic) (?&sp)
(?&token)
)*
)
)
(?:
^ (?&sp) (?= (?&main) (?&sp) $ )
|
(?!^) \G
(?&sp) (?&logic) (?&sp)
)
(?:
\( (?&sp) (?<m_main>(?&main)) (?&sp) \)
|
(?<m_key>(?&key)) (?&sp) = (?&sp) (?<m_value>(?&value))
)
正则表达式 101 上的演示
上面的正则表达式应该与 , 一起使用preg_match_all
,并放在带有 x 标志的分隔符之间(自由间距模式)/.../x
:.
对于每场比赛:
- 如果
m_main
捕获组有内容,则对内容进行另一轮匹配。
- 否则,获取
m_key
和m_value
捕获组中的键和值。
解释
该(?(DEFINE)...)
块允许您定义命名的捕获组,以便在与主模式分开的子例程调用中使用。
(?(DEFINE)
(?<string>'[^']++') # String literal
(?<int>\b\d+\b) # Integer
(?<sp>\s*) # Whitespaces between tokens
(?<key>\b\w+\b) # Field name
(?<value>(?&string)|(?&int)) # Field value
(?<exp>(?&key) (?&sp) = (?&sp) (?&value)) # Simple expression
(?<logic>\b (?:and|or) \b) # Logical operators
(?<main> # <token> ( <logic> <token> )*
# A token can contain a simple expression, or open a parentheses (...)
# When we open a parentheses, we recurse into the main pattern again
(?<token> \( (?&sp) (?&main) (?&sp) \) | (?&exp) )
(?:
(?&sp) (?&logic) (?&sp)
(?&token)
)*
)
)
该模式的其余部分基于这种技术来匹配所有<token>
s in<token> ( <logic> <token> )*
与全局匹配操作。
正则表达式的最后一部分,虽然可以写成(?&token)
,但被扩展以匹配简单表达式中的字段名称和值。
(?:
\( (?&sp) (?<m_main>(?&main)) (?&sp) \)
|
(?<m_key>(?&key)) (?&sp) = (?&sp) (?<m_value>(?&value))
)