在正則表達式的使用上,俺一直在打游擊,是時候來個總結了。由於愚笨等不可抗拒的原因,只能選簡單的說,只寫下對ASCII編碼字符的匹配和php相關的,其他的Unicode的和其他不同流派的,以后碰到了再學習下就行,應付平常的應該夠了。
首先需要接受正則的概念,它是用來尋找文本的一種規則,簡單的文本檢索,比如字符串查找strpos,只是簡單的查找某一串字符的出現,只要稍微變動下需求,比如要查找這樣5個字符,前面兩個是數字,后面3個是字母,且忽略大小寫,如果單純用字符串查找等方法做,顯得很麻煩,而用正則表達式就無比簡單了。正則很強大很靈活,當然靈活的東西一般規則也有點多。在平常生活中,不知不覺會用到正則類似的東西,比如搜F盤下的所有文本文件:*.txt,或者在某些編輯器如Sublime Text,對正則超找提供了支持,又或者在linux命令中也經常用到:find . -name '*.log' -print查找當前目錄下擴展名為log的文件,或者查詢日志時cat 2015-10-18-log |grep 'show_list'(這個不是,只能說類似):打印出記錄了show_list接口的行等等,以及Apache的RewriteRule對訪問鏈接的控制,這些都類似或者就是正則表達式的應用。
在php中,用來支持正則的有三種引擎:preg、ereg、mb_ereg,所謂引擎簡單理解就是底層一些對php在進行正則匹配查找時進行支持的庫和接口,不同的庫有不同的名字,現在用的多是preg,它在功能和速度上要強於其余兩者。preg引擎(或者說套件),即“Perl的正則表達式”(Perl Regular Expression),源於某大牛對當時的ereg套件在性能上的不滿,想做一個更好的庫,於是查閱Perl處理正則的源代碼,但是在他之前另外一大牛,也遇到過這個問題,此大牛(后者)研究了一下Perl正則源代碼,覺得繁瑣復雜,於是自己寫了一套兼容Perl的正則庫PCRE(Perl Compatible Regular Expression),編寫清晰,效率出眾,文檔完備,然后前一位大牛將其改寫到php中,就成了preg,一直慢慢改進到今天。所以preg兼容PCRE,PCRE兼容Perl的正則,preg就是Perl正則的親戚,以上為歷史。
一個簡單的正則表達式示例:'/abc/',表示匹配一個字符串中的abc三個字符,完全等價於普通的字符串查找方法,正則就是一個字符串,只是特殊的符號有特殊的含義而已,而一般將這串表示正則的字符串稱為:模式(pattern)。
1. 定界符
在定義一個正則表達式時,首先要有定界符,表示表達式從這兒開始,到那兒結束,最常用的是/,如匹配abc的'/abc/',從a開始,到c結束。在php中,還可用其他的,比如#、!、{}(左邊用{,右邊用})等,全憑個人習慣。
2. 原子
原子是正則表達式中最基本的單位,細分為5類,俺將伴隨它的功能來記錄。
首先,可以用最普通的字符作為原子,如a、B、c、1、2、_等
'/9527/' '/misson failed/' '/PHP_VERSION/'
一些特殊字符和元字符(metacharacter)也可以,任何一種符號都能在模式中使用,前提是別跟它自己的表示特殊含義的符號相沖突,前面說過,正則很靈活很強大,強大在於有這些特殊含義符號的輔助(有什么特殊作用后面再說),如果正則中要匹配這個在這里有特殊含義的符號,需要轉義(escape),很好理解,就跟雙引號字符串中有雙引號也要轉義一樣。這些特殊符號包括.、*、?、+、‘、"、\ 、/ 等等,其實單引號、雙引號有時不必轉,全看你寫的表達式用的是單引號字符串還是雙引號字符串,在php中為避免出錯寫正則一般用單引號字符串。還有就是別跟自己的定界符沖突,否則系統以為表達式提前結束而出錯。
'/a\.b\?c\+/' '/ab\/123/'
非打印字符作原子,非打印字符泛指空白字符,即空格、水平制表符\t、垂直制表符\v、回車符\r、換行符\n等,注意此處易埋bug,php用單引號字符串,減少出錯
'/\r\n/'
前面說過,可以匹配一般的單個字符,如9527,9后面接着一個5,再接着一個2,然后是7,現在匹配的並不限於這幾個數字,比如我要匹配兩個數字,只要是數字就行,對於這種帶有通用性質的字符,正則也有表示某一類字符的的表示形式,比如表示數字用\d,表示一個0到9的十進制數字,而\D(大寫形式)表示非十進制數字,\w表示匹配一個單詞,在php的正則流派里邊,單詞的定義是大寫字母A-Z,小寫字母a-z,數字0-9以及下划線_,相應的\W大寫形式表示相反的意義。
'/\d\d/' // 匹配兩個數字 '/\D/' // 匹配一個除數字之外的任意字符 '/\w/' // 匹配一個詞 '/\W/' // 匹配一個除了詞的任意字符 '/\s/' // 匹配一個空白字符 '/\S/' // 匹配一個非空白字符
3.元字符
元字符就是有特殊意義的字符,如*、+、?、. 、|、^、$等,一般不能單獨出現,只有在修飾它前面的一個(或一些)原子時才表現出它自己的特殊含義,也就是說,它要配合上面說的原子使用才有意義。它有特殊意義,但就是要匹配它時,就得使用轉義符\了,轉為普通字符。
?:量詞,有或者沒有
現在要匹配一個單詞意思顏色,有兩種寫法,colour或color,中間的u要么有1個,要么沒有,元字符?就適合,而且,如果沒有加括號限制,元字符量詞這類元字符只對位於它前面一個原子起作用,這里?只作用於u
'/colou?r/'
+:量詞,一個或多個
比如匹配一個或多個數字,注意元字符修飾的原子有可能是個序列,但未加括號限制只對一個有效
'/\d+/'
*:量詞,0個、1個或多個,即任意數量
'/\d*/'
區間:規定重復出現次數
前面的次數限制畢竟比較死板,來個活的,區間用一對{}表示,{n}表示出現n次,{n,}表示大於或等於n次,{m,n}表示至少出現m次,至多出現n次,最好m小於n,別故意為難系統~
'/auth{0,1}/' // 出現0次到1次,即? '/auth{1,}/' // 至少出現1次,即+ '/auth{3}/' // h要出現3次
匹配任意字符
.,點號,匹配任意字符,在php中,默認情況下除了換行符,它可以匹配任意任意一一個字符,另外一種情況下,它就真的匹配任意字符,包括換行符,后面再說。
多選結構
|,表示或,依靠它可以生成一個多選分支,需要注意的是|在正則中優先級最低,,下面的不是|作用的不是左右的r和c,而是|左右的子表達式,哪怕把一個表達式寫成'/\d+\s*abc{2,5}|ack?\d/',它仍作用於它前面和后面的子表達式\d+\s*abc{2,5}和ack?\d,只要沒有加括號限制
'/color|colour/' // 匹配color或colour
字符組
匹配若干字符之一,現在要匹配abc中任一個字符,可以這樣:[abc],包在一堆中括號里邊,加入要匹配大寫A到大寫Z任一個字符,可以這樣:[A-Z],中間一個連字符在字符組里邊就成了有特殊意義的元字符表示從什么到什么,,其他還有數字[0-9],或者只是數字、字母的部分值:[2-5]、[c-h],有幾個需要注意的地方:
1. 連字符-只有在字符組內部,且在兩個字符之間才被認為是有效的元字符,出了字符組跟a一樣是普通字符;
2. 如果字符組內部確實需要匹配-,最好將它放在字符組內最前面,如[-a-z],匹配小寫字母或-,在php中也可以將它放在最后,如[c-k-](居然沒報錯-_-#),但放在最前面還是最保險;
3. 對於系統不認可的由-標識的順序字符,對於php會報Compilation failed警告,如[a-9]、[9-0]
4. 字符組的順序一般為從小到大,不能[9-0],大小寫一般分開寫[A-Za-z],但在php中,至少俺這個5.5版本可以大小寫混合寫[A-z],表示匹配從A到Z和a到z的字母,不提倡這樣做,其他語言流派肯能不支持
'/C[EFIMT]O/' '/[A-Za-z0-9_]/' // 相當於\w,表示單詞 '/[a-z]+/' // 匹配一個或多個小寫字母
排除型字符組
前面是匹配任意一個[]內的字符,現在恰好相反,不匹配任意一個字符[]內的字符,如[^a-z]:不匹配任意一個小寫字母,在字符組內最前面加^表示取反。一個有意思的例子:
'/abc[^ABC]/' 字符串1: 'abcK' 字符串2: 'abc' // 匹配?
字符串1很顯然,字符串2匹配嗎?注意字符組:它匹配未出現的字符。這里abc后面要跟一個除A、B或C的字符,但是不能沒有,這便是字符組的坑。
單詞邊界
有時為了匹配一個完整單詞,一般單詞給人印象是左右帶有空格,專門有一個表示單詞的這種邊界元字符\b,注意它並不匹配一個空格,而是一個位置,所以單獨的'abc'也被看作一個單詞,雖然它左右並沒有空格,關於匹配位置后面再詳說。但是正則中,至少目前對單詞的定義還沒那么強大,就是大小寫字母、數字加下划線,跟單詞邊界相反的就是\B,只要不是單詞邊界都能匹配
'/\b\w+\b/' // 匹配一個單詞 'hello world' // 匹配hello(只匹配一次的話) 'abc' // 匹配abc
括號限定
括號的第一個強大功能,是限定元字符的作用范圍,將表達式分成一個個子表達式,如'/col(ou)?r/',加了括號后,?元字符作用的對象是ou而不再只是u,'/abc|def/'指的是匹配abc或者def,但a(bc|de)f匹配的是abcf或者adef,類似編程語言括號運算符,括號內是一個小的單元,相當於一個大原子,當然它還有一個重要作用:分組,后面再說。
行的開頭與結尾
每一行字符串都有一個開頭和結尾,開頭和結尾指一個位置,雖然我們在描述一串字符串會說以...結尾,但正則中不指具體字符,匹配的是位置。比如'/^a.+/',匹配以a開頭的字符串,^表示開頭,$表示結尾,下面是幾個值得注意的例子
'/^/' // 匹配一個行開頭,只要是一行字符串都有開頭,哪怕空字符串,無實際意義 '/^$/' // 匹配一個行開頭,緊接着是行結尾,即匹配一個空行 '/^hello$/' // 匹配以hello開頭的字符串,隨后改行便結尾,即匹配只有hello字符串的行,該行再無其他字符 '/^hello.*hello$/' // 匹配一個行開頭,接着是hello,接着可能是若干其他字符,然后是hello,緊接着是行結尾,即匹配一個一hello開頭,hello結尾,中間有若干字符的字符串
注意在默認情況下,脫字符^和美元符號$表示匹配一個字符串的開頭和結尾,比如"hello",結尾在緊挨着o的右邊,那么"hello\nabc"(在php的雙引號字符串中,\n是元序列,表示換行符,但php的單引號字符串\n只是普通字符,一個\一個n,特此說明)該看做一行還是兩行呢?答案是,在preg中,默認情況下,仍被看做一行,它的結尾是緊挨着c的右邊,可以驗證下
<?php $pattern = '/hello$/'; // 以hello結尾 $subject = "hello\nabc"; // 匹配字符串的結尾,而不是邏輯行的結尾 preg_match($pattern, $subject, $match); echo 'match=><pre>'; var_dump($match); // 無匹配內容
我們把"abc\n"稱為一個邏輯行,把"abc\ndef\n"稱為有兩個邏輯行,因為邏輯上它是有兩行字符串的,但preg默認不匹配多個邏輯行(可能是坑),管你幾個換行符就當一行字符串看,$直接匹配到這個字符串最后一個位置。那么php的正則能不能匹配多個邏輯行呢?當然行,要用到模式修飾符,后面再說。
除了以^表示行開頭,$表示行結尾,在preg中,\A也表示開頭,\z和\Z都表示結尾,不同的是\Z能匹配到最后的換行符,而\z不行。
3. 分組和捕獲
一個簡單的例子,匹配以單引號或雙引號開頭的字符串,結尾需要是對應的雙引號或單引號(中間不存在轉義過的雙引號或單引號)
'/["\'].*["\']/'
如果是上面那樣,第一個字符組匹配了雙引號,是無法保證第二個字符組匹配相同的雙引號,即現在的要求是,前面那個字符組匹配了什么,后面那個字符組也需要匹配相同的字符,不是給一個相同的正則表達式就行。
前面說過括號的第一大作用,限定某些元字符的作用范圍,括號的另一大作用就是分組捕獲,這是正則的特性。比如'/ab(cd)ef(gh)ij/',有兩個括號,preg引擎會對每個括號中匹配到的文本進行記錄,以左括號由左往右數,以數字編號,\1記錄的是第一個左括號所在的里邊的內容,\2記錄第二個左括號所在的里邊的內容,第n個左括號對應\n,preg最多可記錄4096個(強大!),這里\1對應cd,\2對應gh。括號起到了分組的作用,而類似\1、\2...\n稱為反向引用。注意:引用的是正則匹配到的文本,而不是引用的正則表達式。
$pattern = '/(\w)(\d)(.*)/'; // 匹配一個詞,一個十進制數字和若干任意字符 $subject = 'a57h'; preg_match($pattern, $subject, $match); echo 'match=><pre>'; var_dump($match);
結果:
preg_match方法將捕獲的文本放在$match參數中,數組索引的1、2、3對應的元素分別是捕獲到的\1、\2、\3三個分組文本(索引0列出的是整個表達式匹配到的文本)。
對於括號嵌套的,只看左括號的相對順序,比如'/(abc(def)g(hij))(k)/',有效的左括號為4個,\1對應abc的左括號整個括起來的內容(abcdefghij),也包括里邊嵌套括號匹配的內容,\2對應def,\3對應hij,\4對應k。
所以對於上邊引號對應的例子可以是這樣的:'/(["\'].*)\1/',后面的字符要與第一個匹配到的文本一致(注意不是模式一致)。
但是新的變種又來了,有的想分組捕獲,但有的只想分個組,限定下范圍,不想捕獲,?:來了,(?: ... ),在分組的括號最前面添加問號冒號,就表示取消文本捕獲。因此對於'/(?:abc(de)fg(?:hij))/',\1捕獲的是de,沒有\2。
除了數字捕獲(\后面跟數字來標識捕獲內容),還可使用命名捕獲,即給捕獲到的文本取名字,用法(?P<name>...),對括號添加?P<name>,name是取的名字,有的書上寫着(?P=name),5.5親測不行。
$pattern = '/\w(?P<key1>\w\w)\s+(?P<key2>\d+)/'; $subject = 'abcd 233'; preg_match($pattern, $subject, $match); echo 'match=><pre>'; var_dump($match);
結果:
在php的命名捕獲時,把原來數字形式的捕獲也記錄了,key1與\1對應,key2與\2對應,反正多一種不嫌多。
捕獲的另一個巨大的用處,就是在替換操作時,對匹配的文本的引用,比如現在有表達式 '/123(\d\d\d)ok([a-z][a-z])end/',括號捕獲到三個數字和兩個小寫字母,我們想提取出來,怎么辦,就preg來說,使用簡單的替換方法:preg_replace。一個例子
$pattern = '/123(\d\d\d)ok([a-z][a-z])end/'; $subject = '123233okhiend'; $replacement = '$1---$2'; // 在替換字符串中,對捕獲到文本的引用 $ret = preg_replace($pattern, $replacement, $subject); echo 'ret=><pre>'; var_dump($ret);
括號捕捉到了233和hi,如果是反向引用我們知道是\1和\2,替換的字符串$replacement中,使用$1、$2來捕捉它,注意\1、\2是用在原始的正則表達式中的,這里是替換字符串中,當然,替換操作的是字符串的副本。
結果:
如果用命名捕獲來操作,使用對應的$name來引用,親測貌似不行,估計是怕跟上下文中的變量相沖突,但是上面說了,在命名捕獲時,數字索引仍然是有效的,所以在命名捕獲時,我們仍然可以再替換字符串中通過$1來引用匹配到的文本。
一個需要注意的問題:假設我捕獲到了第1組,替換文本是'$15',1后邊又跟着個數字,容易出錯,為提高解析效率,可使用{}將數字括起來,如'${1}5',另,這對命名捕獲仍無效。
4. 環視
前面的行開頭、結尾是對位置的匹配,環視( lookaround)也是對位置的匹配,對於這種位置匹配,可能一個更熟悉的名字是:零寬斷言。^、$、\A、\Z、\b、\B都算零寬斷言,包括環視結構,當然行開頭和結尾也多稱為錨點。比如俺曾做的一個小項目中,有這樣的需求,使用一個別人搭建的框架,每一個model類處理一張表,表名根據model類的類名確定,它的類名一般是這樣的class externalLinks extends Model{...},子類名是這樣externalLinks,它的表名是gw_external_links,gw_不說,從externalLinks到external_links需要在左邊是小寫字母,右邊是大寫字母的地方插一個_,再轉小寫,而且類名不一定只有這樣的,比如abcDefGhi,可能是3個大小寫單詞,個數不確定,用字符串的替換顯得比較麻煩,因為個數不確定,用正則輕而易舉就解決了。凡是這種某地方的左邊是啥,右邊是啥,明顯的位置要求的,都可以用環視試一下。環視分為四種:
肯定順序環視:(?=...),匹配某個位置,它的右邊是...
肯定逆序環視:(?<=...),匹配某個位置,它的左邊是...
否定順序環視:(?!...),匹配某個位置,它的右邊不是...
否定逆序環視:(?<!...),匹配某個位置,它的左邊不是...
不看...就是環視的符號定義,順序、逆序的區別在於一個從左往右看,一個從右往左看。環視在位置檢測時非常有用。例如現在要匹配abc,它的右邊必須是數字,可以這樣
'/abc(?=\d)/'
說說俺自己上面那個問題,找到左邊是小寫字母,右邊是大寫字母的位置,可以這樣
'/(?=[A-Z])(?<=[a-z])/'
如果左右兩邊都需要檢測的,像這種,順序環視跟逆序環視的左右順序的順序不用管,哪個放左邊哪個放右邊都行(等價於'/(?<=[a-z])(?=[A-Z])/')。一般來說逆序環視放左邊,順序環視放右邊,一個好習慣是以位置為中心,想象這個位置左邊應該有什么,右邊應該有什么。上面的模式找到位置了用_替換(實際是插入)掉問題基本就解決了。再比如
$pattern = '/\w+(?=\d)/'; $subject = 'abcd 233'; preg_match($pattern, $subject, $match); echo 'match=><pre>'; var_dump($match);
結果:
想想為啥?匹配一個或多個詞,它的右邊必須是數字,只有'23'符合,它的右邊恰好為數字,2的前面是空格,不屬於\w范圍,而'233'的后面啥都沒有。
注意:只匹配位置,不匹配實際文本,這個有時在多次匹配時有很大區別。
對於單詞邊界符\b,也可用環視寫出來,單詞的開始邊界是左邊非單詞右邊為單詞:(?<!\w)(?=\w),單詞的結束邊界是左邊為單詞右邊非單詞:(?<=\w)(?!\w),合在一起就是:(?<!\w)(?=\w)|(?<=\w)(?!\w)(|,或的左右表達式是整體,它的優先級最低)。
5. 占有優先 vs 忽略優先
標准量詞都是匹配優先的,如*、+、?以及區間量詞{m,n},它們總是在自己的范圍內盡可能多的匹配字符,對於{m,n}(m<n)有n個的話絕不止匹配m個就完事兒。例如
$pattern = '/\w+(?=\d)/'; $subject = 'hello123'; preg_match($pattern, $subject, $match); echo 'match=><pre>'; var_dump($match);
結果:
上例在匹配單詞時總是盡可能匹配最多,所以單看\w+會一直匹配到字符串結尾3處,但隨后的環視檢查出現右邊不是一個數字(什么都沒有),於是交還一個詞,再看看右邊是不是數字,這下是了(hello12)然后就匹配成功返回,這里有一個回溯的過程(下一篇寫寫),不細糾結,有的書稱這種匹配優先為貪婪模式,即標准量詞默認都是貪婪的,盡可能多的去匹配的,那么與之對應的就有非貪婪模式。
在量詞后面加個問好,表示非貪婪模式,即忽略優先,如*?、+?、??、{m,n}?,非貪婪模式下的量詞總是盡可能匹配少的字符,還是上面那個例子,如果把$pattern改為下面呢?
$pattern = '/\w+?(?=\d)/'; // 忽略優先
匹配的結果是'hello',過程是首先+?使得\w每匹配一個詞就檢查下右邊是不是數字,是的話匹配就此停止,返回結果,要保證匹配到的字符最好嘛,不是的話繼續匹配,直到右邊是數字便立即停止返回。忽略優先可以提前返回結果,當然可能不是最優的,提高匹配速度(下一篇再總結下)。
有木有倔強的不交還的列?當然有,這就是占有優先,它在標准量詞后面加一個+,如*+、++、?+、{m,n}+,把上面的例子再改改,有意向不到的結果-_-#
$pattern = '/\w++(?=\d)/'; // 占有優先
記住占有優先的特點:不交還!\w+一直匹配到hello123結尾,發現之后啥都沒有,失望至極,然后停止匹配,報告匹配失敗了,沒錯這次居然是匹配失敗了!占有優先仍屬於匹配優先,盡可能多的去匹配,只是匹配到最后如果發現某條件不符,不好意思,他不服,直接停止。所以占有優先可以提前報告失敗,提高匹配速度。
忽略下占有優先的脾氣,來看一個新東西:固化分組。表示方式:(?>...)(它應該屬於分組捕獲的,俺覺得放這兒更好),同占有優先功能一樣,它也是優先匹配,並且不交還。所以再把上面的$pattern改為這個樣子,也是匹配失敗滴。
$pattern = '/(?>\w+)(?=\d)/'; // 固化分組
6. 模式修飾符
模式修飾符(pattern modifier),放在模式的最后面,結束定界符之后,如'/....../imx',i、m、x表示不同的修飾符,可以一次使用多個。模式修飾符是對正則表達式整體效果起一個調節作用。
i:匹配時不區分大小寫,如'/abc/i',對匹配到的abc任一個大小寫都可;
m:將字符串視為多個邏輯行,還記得前面說匹配一行字符串的開頭^和結尾$嗎?默認preg將字符串視為一行,哪怕里邊有有效的換行符,如果加了修飾符m,將帶匹配字符串按照換行符分行,這些行稱為邏輯行,^匹配每一個邏輯行的開頭,$匹配每一個邏輯行的結尾;
s:還記得前面說的匹配任一字符的元字符 . (點號)嗎?preg默認情況下點號匹配任一個字符,除了換行符,那么加了s修飾符,點號就連換行符也不拒絕了;
x:這個修飾符對於復雜的正則表達式非常有用,因為它可以在正則中添加注釋!!!而且它會忽略正則中的空白符,當然,也包括換行,為了說明亂舉一例:
// 使用x修飾符 $pattern = '/(\w+ \d) (?: # 這里匹配啥啥啥 "\w" | "\d" | # 這里應當這么理解那啥 [-A-Z]+ # 這里你自己猜 )/x'; $subject = 'wcwieu2832z28'; preg_match($pattern, $subject, $match);
e:它在特定的地方有用,即preg_replace方法中,preg的preg_replace方法原型如下
mixed preg_replace(mixed $pattern, mixed $replacement, mixed $subject [, int $limit = -1 [,int &$count ]])
第二個參數$replacement,會將匹配到文本替換為它,使用e修飾符,$replacement不僅可以用簡單的文本,不僅可以對捕獲到的文本進行$1式的引用,更流弊的是,可以寫php代碼,以字符串的形式(有點eval的感覺),例
$ret = preg_replace('/\d([A-Z]+)/e', 'strtolower("$1")', '5BBC'); echo 'ret=><pre>'; var_dump($ret);
可是當俺准備看結果時,報錯:The /e modifier is deprecated, use preg_replace_callback instead,納尼!模式符e被棄用了,讓用preg_replace_callback這個方法,它使用一個會調函數(異步調用)來處理匹配到的結果,顯然比把代碼直接堆在字符串里好多了,具體怎么用可以參考手冊。
D:設定了此修飾符的話,模式中的$只會匹配字符串的結尾(EOS,End Of String),而不是EOS前面的換行符,即不會看做多行。但如果設定了m修飾符將忽略此選項;
U:反轉了 * 跟 *? 的含義,原來的匹配優先變為忽略優先,原來的忽略優先變為匹配優先。除了讓人更加暈菜以外,好像沒個鳥用。
最后,還有個文本模式,不是模式修飾符,表示方式 \Q ... \E。
假如我們就是想匹配一段文本,但是里面夾雜了很多元字符,轉義后看起來會很亂。就可以使用 '/\Q$*.?|/\E/',只是舉個極端的例子,\Q與\E之間的字符,preg一律把它當做普通字符處理,免去轉義之勞。當然也可以手動轉義,更加可以用preg_quote來將這個沒有帶\Q和\E的表達式轉為一個轉義后的表達式。
以上,還有些強大的東西,如正則表達式遞歸,暫時用不着。
以后忘了再來看一眼 >>>逃