最近使用 PHP 寫(xiě)了一個(gè)應(yīng)用,主要是正則表達(dá)式的處理,趁機(jī)系統(tǒng)性的學(xué)習(xí)了相應(yīng)知識(shí)。
這篇文章的寫(xiě)作方式不是講理論,而是通過(guò)具體的例子來(lái)了解正則,這樣也更有實(shí)踐性,在此基礎(chǔ)上再去看正則表達(dá)式的基本概念會(huì)更有收獲。
禁止分組的捕獲
在正則中分組很有用,可以定義子模式,然后可以通過(guò)后向引用來(lái)引用分組的內(nèi)容,但是有的時(shí)候僅僅想通過(guò)分組來(lái)進(jìn)行范圍定義,而不想被分組來(lái)捕獲,通過(guò)一個(gè)例子就能明白:
$str = "http://www.google.com"; $preg= "/http:\/\/\w+\.\w+.(?:net|com|cn)+/is"; $preg2= "/http:\/\/\w+\.\w+.(net|com|cn)+/is"; preg_match($preg,$str,$arr); preg_match($preg2,$str,$arr2);
當(dāng)模式中出現(xiàn)?:表示這個(gè)括號(hào)的分組不會(huì)被引用,運(yùn)行下例子就能明白。
preg_match() 和 preg_match_all() 的區(qū)別
preg_match() 在匹配模式的時(shí)候匹配到一次就結(jié)束,而 preg_match_all() 則進(jìn)行全局匹配,通過(guò)一個(gè)例子就能明白:
$str='hello world china'; $preg="/\w+\s/is"; preg_match($preg,$str,$arr); print_r($arr); preg_match_all($preg,$str,$arr); print_r($arr);
正確理解 $ 和 ^
先說(shuō)一個(gè)正則,為了匹配是否是手機(jī)號(hào):
$str = "13521899942a"; $preg="/1[\d]{3,15}/is"; if (preg_match($preg,$str,$arr)) { echo "ok"; }
雖然字符串中有一個(gè)英文字母,但是這個(gè)子模式卻匹配了,原因就在于模式匹配到后就結(jié)束了,不會(huì)再去尋找英文字母,為了解決這問(wèn)題 $ 和 ^ 就發(fā)揮作用了,比如讓字符串的開(kāi)始和結(jié)尾必須匹配一定的模式,修改如下:
$str = "13521899942a"; $preg="/1[\d]{3,15}$/is"; if (preg_match($preg,$str,$arr)) { echo "ok"; }
$ 和 ^ 的跨行模式
默認(rèn)的情況下,$ 和 ^ 只會(huì)匹配完整段落的開(kāi)始和結(jié)尾,但是通過(guò)改變選項(xiàng),允許匹配文本的每一行的開(kāi)始和結(jié)尾,通過(guò)下面的例子就能明白
$str='hello world'; $preg='/\w+$/ism';//$preg='/(?m)\w+$/is'; preg_match_all($preg,$str,$arr); print_r($arr);
分組命名
在正則中通過(guò)括號(hào)分組后,可以使用 \1,\2 這樣的數(shù)字進(jìn)行后向引用,但是假如正則中模式太多,在使用的時(shí)候就會(huì)比較混亂,這時(shí)候可以采用分組命名來(lái)進(jìn)行引用,看個(gè)例子:
$str ="email:ywdblog@gmail.com;"; preg_match("/email:(?<email>\w+?)/is", $str, $matches); echo $matches["email"] . "_" . $matches['no'];
懶惰模式
正則在匹配的時(shí)候是貪婪的,只要符合模式就會(huì)一直匹配下去,下面的例子,匹配到的文本是 <h2>hello</h2><h2>world</h2>
$str = "<h2>hello</h2><h2>world</h2>"; $preg = "/<h2>.*<\/h2>/is"; preg_match($preg,$str,$arr); print_r($arr);
通過(guò)改變一個(gè)選項(xiàng)可以修改為懶惰模式,就是一旦匹配到就中止,修改代碼如下:
$str = "<h2>hello</h2><h2>world</h2>"; $preg = "/<h2>.*?<\/h2>/is"; preg_match($preg,$str,$arr); print_r($arr);
進(jìn)一步理解 preg_match_all()
通過(guò)這函數(shù)的最后一個(gè)參數(shù),能夠返回不同形式的數(shù)組:
$str= 'jiangsu (nanjing) nantong guangdong (guangzhou) zhuhai beijing (tongzhou) haidian'; $preg = '/^\s*+([^(]+?)\s\(([^)]+)\)\s+(.*)$/m'; preg_match_all($preg,$str,$arr,PREG_PATTERN_ORDER); print_r($arr); preg_match_all($preg,$str,$arr,PREG_SET_ORDER); print_r($arr);
強(qiáng)大的正則替換回調(diào)
雖然 preg_replace() 函數(shù)能完成大多數(shù)的替換,但是假如你想更好的控制,可以使用回調(diào),不用多說(shuō)看例子:
$str = "china hello world"; $preg = '/\b(\w+)(\w)\b/'; function fun($m){ return $m[1].strtoupper($m[2]); } echo preg_replace_callback($preg,"fun",$str);
在這一點(diǎn)上,PHP 比 Python 強(qiáng)大的多,Python 中沒(méi)有正則回調(diào),不過(guò)可以使用閉包的方式解決,可看我以前的文章。
preg_quote()
這個(gè)函數(shù)類(lèi)似于 Python 中的 re.compile() 函數(shù),假如在模式中一些元字符僅僅想表達(dá)字符的本身含義,可以轉(zhuǎn)義,但是假如在模式中寫(xiě)太多的轉(zhuǎn)義,會(huì)顯得很混亂,可以使用這個(gè)函數(shù)來(lái)統(tǒng)一轉(zhuǎn)義:
$str = '\\*china*world'; $preg = "\*china"; $preg = preg_quote($preg); echo $preg; preg_match( "/{$preg}/is",$str,$arr); print_r($arr);
向前查找 ?= 的妙用
用英文解釋可能比較貼切: