通过正则表达式拆分自定义条件

时间:2014-06-10 18:27:58

标签: php regex preg-match-all

我正在忙着编写后端模块,用户可以在其中创建自己的条件/规则。现在我想分成一个条件。

例如:

  

条件:{variable}> 100

// desired output
Array
(
    [0] => {variable} > 100
)
  

条件:{variable} == {anotherVariable}或{var}< = 10

// desired output
Array
(
    [0] => {variable} == {anotherVariable}
    [1] => OR
    [2] => {var} <= 10
)
  

条件:{variable} == {anotherVariable} AND {var}&lt; = 10 AND {variable} == some string

// desired output
Array
(
    [0] => {variable} == {anotherVariable}
    [1] => AND
    [2] => {var} <= 10
    [3] => AND
    [4] => {variable} == some string
)

之后我必须将比较分为三部分:

  

比较: {variable} == {anotherVariable}

// desired output
// allowed comparison operators are: ==, !=, >, >=, <, <=, <>
Array
(
    [0] => {variable}               // comparison
    [1] => ==                       // comparision operator
    [2] => {anotherVariable}        // comparison
)

我现在用正则表达式挣扎了几个小时,但没有达到预期的输出。

1 个答案:

答案 0 :(得分:0)

为了使它更简单,我假设字符串在双引号之间。

模式的第一部分定义了((?(DEFINE)...))具有命名子模式的所需元素。首先是简单元素(比较运算符,布尔运算符,操作数),以及更详细的元素(原子条件,条件,检查)。每个子模式都可以与其他子模式一起定义。

定义部分关闭后,主模式开始并捕获&#34;标记内的三个可能元素&#34;每次迭代时命名捕获:布尔运算符,原子条件(即最可能的基本条件),或括号内的整个条件

要成功,主模式必须通过两个入口点之一,第一个(仅使用一次)是子模式\g<check>。此子模式确保整个字符串从开始到结束都很好,并且由于它是以\A开头的零宽度断言,因此强制令牌搜索从字符串的beginind开始。第二个入口点是(?!\A)\G,意思是&#34;不是在字符串的开头,与先前的匹配&#34;相邻。此条目用于所有其他比赛。

\g<par>命名捕获仅供递归函数使用。如果捕获存在,则令牌是括号内的整个条件,需要重新分析以生成数组。

define('PATTERN', <<<'EOD'
~
(?(DEFINE)
  (?<comp_op> [=!]= | >=? | <[=>]? )
  (?<bool_op> OR | AND )
  (?<operand> { \w+ } | -?\d+ | "[^"]+" ) # you can improve the " pattern

  (?<at_cond> \g<operand> \s+ \g<comp_op> \s+ \g<operand> ) # atomic cond
  (?<cond> \g<at_cond> (?> \s+ \g<bool_op> \s+ \g<cond> )* | \( \g<cond> \) )
  (?<check> (?= \A \s* \g<cond> \s* \z ) ) # checks the whole string format
)

(?: \g<check> \s* | (?!\A)\G \s+)
(?| (?<token>\g<bool_op>) | (\g<at_cond>) | \( (\g<cond>) )
(?<par> \) )? # only to test if a recursion is needed
~x
EOD
);

function getTokens ($str) {
    if (preg_match_all(PATTERN, $str, $matches)) {
        $tokens = array(); 
        foreach ($matches['token'] as $k=>$token) {
            $tokens[] = ($matches['par'][$k]) ? getTokens($token) : $token;
        }
        return $tokens;
    }
    return false;
}

$testset = array(
    '{variable} > 100',
    '{variable} == {zogabu} OR {buga} <= 10',
    '{meuh} == {zo} AND ({meuh} <= 10 OR {zomeuh} == "GABUZO")',
    '{BU} > 3 AND ({ga} == "ZO" OR ({bu} == "GA" AND {meuh} != "GA"))' );

foreach($testset as $test_string) {
    $result = getTokens($test_string);    
    echo "\ntest_string: $test_string\n" . print_r($result, true);
}