正则表达式,PHP:如何否定捕获括号

时间:2011-08-18 05:16:58

标签: php regex

我想用php正则表达式分析一个mysql请求, 也就是说,从mysql语句中提取select_expr和table_references。 例如,这里有两个我希望我的正则表达式匹配的mysql查询:

select id, name from table

select id, name

从该查询中我想提取两部分:“id,name”信息和“table”信息。

第一部分实际上可以包含一个字符串,如CONCAT('id','。','nom')AS别名,

,第二部分看起来像:table t INNER JOIN table2 t2 ON t.id = t2.user_id。

所以我试过这个“我知道它不起作用但会让我上路”正则表达式:

'!select (.*)( from (.*))?!i'

当然,第一个捕捉括号直到结束,这不是我想要的。

select id, name from table

字符串,它匹配“id,nom from table”作为第一部分,这不是我想要的。 (我希望“id,nom”作为第一部分,“table”作为第二部分)。

从这一点来说,我想做的是告诉正则表达式第一个捕获括号 如果成立,则不应与“from”序列匹配。 我知道有一个否定的字符类功能,[^ a-z], 但这只是否定一个字符而不是一个完整的字符串(按正确顺序排列的字母序列)。

你有这个灯吗? 我们可以否定括号的内容,例如使用正则表达式吗?

3 个答案:

答案 0 :(得分:1)

最后一点,如果你的问题听起来像你的查询中的'from'部分是可选的,是吗?

如果是这样,那就试试这个:

!^select (.*?)(?: from (.*))?$!i

这将匹配“select”和“from”之间的所有内容,如果找到“from”,否则它将匹配“select”之后的所有内容。

加入?在“。*?”它告诉'*'不要贪婪,所以当它碰到表达式的其余部分匹配的地方时,它不会继续占用更多的字符。 我还添加了'?:',这使得第二组成为非捕获组,因为没有有用的信息可以从中读取。最后将表达式包装在^和$中以标记行的开始和结束。

如果'from'不是可选的,那么它就容易多了,你可以使用它:

!^select (.*) from (.*)$!i

答案 1 :(得分:0)

问题是你使用greedy matching。也就是说,您的第一个.*组会匹配字符,直到您的正则表达式的其余部分中断。由于FROM子句是可选的,因此它永远不会发生,并且您的第一个组只匹配所有内容。解决方案是使用非贪婪匹配,在?之后添加*(它也适用于+)。

'!select (.*?)( from (.*))?!i'

对于你的简单案例应该足够了。但是,如果要解析整个查询,那么向后解析SQL语句实际上要容易得多。例如,让我们有一个功能齐全的SQL查询:

SELECT foo FROM bar WHERE cond GROUP BY col HAVING stuff ORDER BY this

如果你strrev,你得到:

siht YB REDRO ffuts GNIVAH loc YB PUORG dnoc EREHW rab MORF oof TCELES

考虑到这一点,您可以使用正则表达式轻松地将其拆分,而不会以LISPesque数量的堆叠括号结束。这是我用来匹配这样一个字符串的注释正则表达式(你需要把它放回一行没有空格)。

^ // match the beginning
    (.+\s+YB\s*REDRO)?\s* // is there an ORDER BY?
    (.+\s+GNIVAH)?\s* // is there a HAVING?
    (.+\s+YB\s*PUORG)?\s* // is there a GROUP BY?
    (.+\s+EREHW)?\s* // is there a WHERE?
    (.+\s+MORF)?\s* // is there a FROM?
    .+\s+TCELES // there is a SELECT
$ // match the end

现在,你所要做的只是strrev支持你的结果,并且瞧!你有一个很好的分裂查询。

编辑我们可以使用非捕获组和命名组来增强正则表达式。现在,我们通过比赛获得个别条款;也就是说,它们以关键字开头。如果没有关键字,告诉捕获组中的内容会非常混乱。命名组有助于解决此问题。

非捕获组是未出现在正则表达式结果中的组。它们以?:开头,它们对于使块可选(如(?:stuff here)?)非常有用,而无需在结果中处理它。

这是新的正则表达式。我也只是learned about the x modifier这使得PCRE忽略空格并接受正则表达式中的注释,所以让我们用它来制作一个有效的片段。

$regex = "/^
    (?:(?<orderby>.+)\s+YB\s*REDRO)?\s* # is there an ORDER BY?
    (?:(?<having>.+)\s+GNIVAH)?\s*      # is there a HAVING?
    (?:(?<groupby>.+)\s+YB\s*PUORG)?\s* # is there a GROUP BY?
    (?:(?<where>.+)\s+EREHW)?\s*        # is there a WHERE?
    (?:(?<from>.+)\s+MORF)?\s*          # is there a FROM?
    (?<select>.+)\s+TCELES              # there is a SELECT
$/msix";

$query = "SELECT foo FROM bar WHERE cond GROUP BY col HAVING stuff ORDER BY this";

preg_match($regex, strrev($query), $matches);
foreach ($matches as &$match)
    $match = strrev($match);

// now we can use $matches['from'] to get the FROM clause
echo $matches['from'];

print_r($matches);

答案 2 :(得分:0)

试试这个:

$string = "select id, name, CONCAT('id','.','nom') AS alias as a from table t INNER JOIN table2 t2 ON t.id=t2.user_id";
preg_match_all("!select (.*) from (.*)!i", $string, $result);
var_dump($result);

我刚试过它,它运作得很好。