1

我正在尝试在 PHP 中编写正确的正则表达式来解析字符串(由某些用户编写)来构建请求。它可能很复杂:

name = 'benjo' and (surname = 'benny' or surname = 'bennie') or age = 4

稍后我将解析字符串以构建 mySQL 查询。现在,我只是想找到正确的正则表达式来将此字符串解析为一个可能如下所示的数组:

$result = array(
0 => name = 'benjo',
1 => and
2 => array(
    0 => surname = 'benny',
    1 => or,
    2 => surname = 'bennie',
    ),
3 => age = 4
);

我考虑过使用递归函数,我现在的正则表达式是:

"#\((([^()]+|(?R))*)\)|(ou[^()])|(et[^()])#",

这当然行不通。

如果有人可以提供帮助,我会很高兴,我有点卡在这里!:) Tks,罗曼

让我们改变挑战!:) 好的,现在让我们让它更简单一点。使用正则表达式并添加我们保持在“一级”的约束需要什么!没有嵌套括号,只有一个级别,但仍然有尽可能多的 AND/OR ......这会改变任何有利于或 REGEXP 的东西吗?(我真的很想避免编写我的迷你解析器,尽管这听起来很有趣......

4

1 回答 1

4

理论上的正则表达式不足以进行括号匹配。理论上的正则表达式只能处理左递归/右递归规则。中间递归规则不能用正则表达式表示(例如<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_keym_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))
)
于 2012-05-27T08:28:20.883 回答