2011-08-18 55 views
1

我想分析一個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

回答

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); 
+0

非常複雜,但當用戶嘗試選擇MORFEUS字段時可能會失敗;( – ling

+0

@ling,你說的沒錯,'\ s *'匹配零個或多個空格,我們需要一個或多個。將它改爲\ s +',它匹配一個或多個空格 – zneak

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

我只是測試它,它工作得很好。

+0

它不適用於'select id,name',這是OP想要解析的查詢的第二個例子 – zneak

1

最後一點,如果你的問題聽起來像你的查詢的'從'部分是可選的,是嗎?

如果是的話,那就試試這個:

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

這將匹配之間的「選擇」和「從」,如果「從」被發現,否則它只會匹配後一切「選擇」的一切。

通過添加?在「。*?」中它告訴'*'不要貪婪,所以當它碰到一個表達式匹配的地方時,它不會繼續佔用更多的字符。 我還添加了'?:',這使得第二組成爲非捕獲組,因爲沒有有用的信息可以從中讀取。最後在^和$中包裝表達式來標記行的開始和結束。

如果 '從' 是不可選的,雖然,那麼它是一個極大的方便,你可以用這個!(。*?)。

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

謝謝,這是一個我在尋找 – ling