1. 程式人生 > >利用正則表示式排除特定字串

利用正則表示式排除特定字串

可參考:http://blog.csdn.net/zxy6173/article/details/4229719

正則線上測試:http://www.regexpal.com/

匹配log中包含ERROR但不包含XXX字串的 log

^.*ERROR(?!.*exceed.*)

exceed可替換為任意字串

核心:使用了正則表示式 預搜尋與反向預搜尋的功能

閱讀目錄

回到頂部

查詢不以baidu開頭的字串


baidu.com
sina.com.cn

正則:^(?!baidu).*$  匹配結果就是第2行,也就是第1行被排除了
這裡使用了零寬度斷言(?!exp),注意,我們有一個向前查詢的語法(也叫順序環視)  (?=exp)
(?=exp) 會查詢exp之前的【位置】如果將等號換成感嘆號,就變成了否定語義,也就是說查詢的位置的後面不能是exp
一般情況下?!要與特定的錨點相結合,例如^行開頭或者$行結尾,那麼上面的例子的意思如下:
^(?!baidu).*$ 先匹配一個行開頭的位置,並且要求接下來的位置的後面不是baidu這個字串。這樣由於第一行^之後的位置後面是baidu所以匹配失敗,被排除在外了。

回到頂部

查詢不以com結尾的字串


www.sina.com.cn
www.educ.org
www.hao.cc
www.baidu.com
www.123.com

正則 ^.*?(?<!com)$  匹配前3行結果。
如果查詢以com結尾的字串則使用正則 ^.*?(?<=com)$或者 ^.*?com$  
對正則表示式的解釋:^.*?(?<!com)$
首先匹配行開頭,然後是 .*? 這個是忽略優先,也就是優先忽略不匹配任何字元,(?<!com) 這個是一個逆序環視的否定形式,意思是匹配一個位置此位置的前面不能是字串com,最後是一個行結束。對於www.123.com來說,首先匹配行首,接著匹配w後面的位置,發現前面不是com,所以成功但緊接著要匹配行尾,失敗,回溯讓.*? 匹配一個w符號,接著(?<com)匹配第二個w後面的位置,發現前面也不是com匹配成功,緊接著要匹配$對應的行尾失敗,一直到.*?匹配了www.baidu.com的時候,此時(?<!com)匹配m後面的位置,此時此位置的前面是com匹配直接失敗,接著.*?匹配行末尾,(?<!com)匹配$後面的位置,顯然這次也失敗了,所以整個全域性匹配都失敗。  www.123.com被排除到匹配之外。這裡的.*後面加不加問號結果都一樣。

回到頂部

查詢不含有if的行


if (a>b)
printf("hello");
else if(a<b)
printf("hello2");
else
printf("hello3");

正則 ^([^f]|[^i]f)+$
其實這個匹配也是一個排除型字串的匹配,但是不同於上面兩種,因為這裡的if可能既不在行開頭,也不在行結尾,而是在字串中間這樣就給匹配帶來了麻煩,在正則表示式中沒有提供類似排除的功能。我們最容易想到的就是下面的正則:
^[^if]+$ 這種寫法看起來是那麼回事,但是排除型字元組排除的是i和f兩個字元,而不是if這個字串,所以這個正則表示式匹配的是那些既沒有i字元也不包含f字元的字串。但是如果字串中有一個i或多個i或者有一個或多個f,或者i和f字元都有隻不過沒有連在一起。這些情況都是我們需要匹配的情況,而我們不能匹配的情況是那些包含if字串的行,而不是包含i或f字元的行,所以這種寫法漏洞很大。

^.*(?!if).*$ 這種寫法使用了零寬度斷言,表面意思看起來好像是說 任意字元+非if+任意字元 組成了整個字串,但是仔細研究匹配過程就知道這個是錯的,(?if)匹配的是一個位置,所以對於字串aifb他也是可以匹配到的,而實際上這樣的字元正是我們不要的。按照這個正則表示式,對於aifb 首先匹配行首,其次.*是貪婪模式(匹配優先),會一直匹配到字串的末尾(此時傳動裝置定位在$位置前面),此時(?!if)需要匹配一個位置,這個位置的後面不能是if,這個時候正好位置在b字元的後面,符合匹配條件,緊接著匹配行尾,到這裡整個全域性匹配成功。

也就是說對於一個字串例如我要排除abc這個字串,那麼對於任意一個字串   helloworld abc helloworld 在匹配的時候(?!abc)可以匹配h、e、l、l、o、w、o、r、l、d等這些字元後面的位置,都是成功的。所以匹配根本還沒有進行到abc這個地方,(?!abc)就會匹配成功。這個時候根本起不到排除的作用,為什麼上面的第1和第2個例子可以呢,因為他們的位置有行首和行尾限定。例如我要匹配行首不是abc的話,那麼此時^(?!abc) 這個時候(?!abc)實際上在匹配的時候其傳動裝置的位置被行首進行了限定,所以對於那些以abc開頭的字串來說就會匹配失敗了。

對於正則表示式^.*(?!abc).*$怎麼能讓第一個.*匹配到 helloworldabcxxx中的helloworld的問題。

對於上面的題目,我們的答案是^([^f]|[^i]f)+$  其實就將所有的匹配分成了2種情況,一種情況是假設字串中沒有f字元,    自然就不可能有if字串了,這種情況下匹配的字串中是不可能有if的。第二種情況就是有f字元,但是我們要求此時f的前面不能是i,所以在有f和沒有f兩種情況都考慮到的情況下,這個正則就應該可以滿足所有的情況了。

其實這個問題的解答是不完美的,對於排除的字串if只有2個字元i和f字元,我們可以使用這種方式,但是如果我們要排除的是字串helloworld,這種方法顯然就不實用了,那要考慮到多少種情況呢?

排除不含有某字串的最終方案

:在這種情況下我們使用  ^(?!.*helloworld).*$  正則表示式  我們將第一個.*移到了零寬度斷言的裡面。在匹配的時候首先匹配行首的位置,然後接下來是匹配行首後面的位置,要求此位置的後面不能是    .*helloworld 匹配的字串,說白了要求此位置的後面不能是xxxxxxxxxxxxxxxxxxhelloworld 類似的字串,這樣就排除了從行首開始後面含有helloworld的情況了。

預搜尋:

在看正則的時候中碰到一個這樣的正則問題.
將 一句SQL語句中的“select”和“from” 之間的字元替換為 “count(*)”
問題很簡單,我的答案是

代 碼:
<?php
$sql = "select uid,sas,fd from asdf";
echo preg_replace("/select(.+?)from/i",'select count(*) from',$sql);
?>


但是看了別人的答案卻十分的簡單

程式碼:
echo preg_replace("/(?<=select).+?(?=from)/i",' count(*) ',$sql);


其中的?<=讓我很不解.從資料上查得到:

程式碼:
(?<=select).+?(?=from)表示的是匹配從select開始到from結束中間的資料.

正向預搜尋:"(?=xxxxx)","(?!xxxxx)"
反向預搜尋:"(?<=xxxxx)","(?<!xxxxx)"
正向預搜尋:"(?=xxxxx)","(?!xxxxx)"

格式:"(?=xxxxx)",在被匹配的字串中,它對所處的 "縫隙" 或者 "兩頭" 附加的條件是:所在縫隙的右側,必須能夠匹配上 xxxxx 這部分的表示式。因為它只是在此作為這個縫隙上附加的條件,所以它並不影響後邊的表示式去真正匹配這個縫隙之後的字元。這就類似 "\b",本身不匹配任何字元。"\b" 只是將所在縫隙之前、之後的字元取來進行了一下判斷,不會影響後邊的表示式來真正的匹配。

舉例1:表示式 "Windows (?=NT|XP)" 在匹配 "Windows 98, Windows NT, Windows 2000" 時,將只匹配 "Windows NT" 中的 "Windows ",其他的 "Windows " 字樣則不被匹配。


舉例2:表示式 "(\w)((?=\1\1\1)(\1))+" 在匹配字串 "aaa ffffff 999999999" 時,將可以匹配6個"f"的前4個,可以匹配9個"9"的前7個。這個表示式可以讀解成:重複4次以上的字母數字,則匹配其剩下最後2位之前的部分。當 然,這個表示式可以不這樣寫,在此的目的是作為演示之用。

格式:"(?!xxxxx)",所在縫隙的右側,必須不能匹配 xxxxx 這部分表示式。
舉例3:表示式 "((?!\bstop\b).)+" 在匹配 "fdjka ljfdl stop fjdsla fdj" 時,將從頭一直匹配到 "stop" 之前的位置,如果字串中沒有 "stop",則匹配整個字串。
舉例4:表示式 "do(?!\w)" 在匹配字串 "done, do, dog" 時,只能匹配 "do"。在本條舉例中,"do" 後邊使用 "(?!\w)" 和使用 "\b" 效果是一樣的。

反向預搜尋:"(?<=xxxxx)","(?<!xxxxx)"

這兩種格式的概念和正向預搜尋是類似的,反向預搜尋要求的條件是:所在縫隙的 "左側",兩種格式分別要求必須能夠匹配和必須不能夠匹配指定表示式,而不是去判斷右側。與 "正向預搜尋" 一樣的是:它們都是對所在縫隙的一種附加條件,本身都不匹配任何字元。
舉例5:表示式 "(?<=\d{4})\d+(?=\d{4})" 在匹配 "1234567890123456" 時,將匹配除了前4個數字和後4個數字之外的中間8個數字。