用正则表达式编写PHP查询解析器

时间:2012-05-27 08:09:19

标签: php mysql regex

我正在尝试在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[^()])#",

当然不起作用。

如果有人可以提供帮助,我会很高兴,我有点被困在这里! :) 韩国社交协会, 罗曼

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

1 个答案:

答案 0 :(得分:4)

理论正则表达式不足以进行括号匹配。理论正则表达式只能处理左递归/右递归规则。中间递归规则不能用正则表达式表示(例如<exp> -> "(" <exp> ")")。

然而,编程语言中的正则表达式实现了允许正则表达式超出常规语法功能的功能。例如,正则表达式中的反向引用允许人们编写匹配a non context-free languages的正则表达式。但是,即使使用反向引用,仍然无法使用正则表达式来平衡括号。

由于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))
)

Demo on regex101

上面的正则表达式应该与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) 
    )*
  )
)

该模式的其余部分为based on this technique,以匹配<token>中具有全局匹配操作的所有<token> ( <logic> <token> )*

正则表达式的最后一部分虽然可以写成(?&token),但会扩展为匹配简单表达式中的字段名称和值。

(?:
  \( (?&sp) (?<m_main>(?&main)) (?&sp) \)
  |
  (?<m_key>(?&key)) (?&sp) = (?&sp) (?<m_value>(?&value))
)