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

问题描述:

我想分析一个MySQL正则表达式的请求, ,即从mysql语句中提取select_expr和table_references。 例如,这里有两个,我想我的正则表达式匹配的MySQL查询:正则表达式,PHP:如何否定一个捕获括号

select id, name from table 

select id, name 

从该查询我想提取2个部分:“ID,名称为”信息和“表”信息太多。

第一部分实际上可以包含像CONCAT一个字符串( 'ID', 'NOM' '')AS别名,

和第二部分可以是这样的:表t INNER JOIN table2的T2 ON吨.ID = t2.user_id。

所以,我想这个“我知道这不是工作,但会让我在路上”的正则表达式:

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

当然,上述第一捕捉括号得到所有,直到最后,这是不是我想。

select id, name from table 

字符串,它匹配“从表ID,NOM”的第一部分,这不是我想要的。 (我想在这种情况下第一部分是“id,nom”,第二部分是“table”)。

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

你有什么灯光吗? 我们可以用正则表达式否定括号内容吗?

+1

是” ^选择(?:从(*) )?$!我工作? – loganfsmyth

+0

哦,是的,谢谢,我会调查那个(?:)符号。真的很酷。 – ling

问题是您使用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); 
+0

非常复杂,但当用户尝试选择MORFEUS字段时可能会失败;( – ling

+0

@ling,你说的没错,'\ s *'匹配零个或多个空格,我们需要一个或多个。将它改为\ s +',它匹配一个或多个空格 – zneak

尝试了这一点:

$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); 

我只是测试它,它工作得很好。

+0

它不适用于'select id,name',这是OP想要解析的查询的第二个例子 – zneak

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

如果是的话,那就试试这个:

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

这将匹配之间的“选择”和“从”,如果“从”被发现,否则它只会匹配后一切“选择”的一切。

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

如果 '从' 是不可选的,虽然,那么它是一个极大的方便,你可以用这个!(。*?)。

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

谢谢,这是一个我在寻找 – ling