在電腦上我們經常會使用(通配符)找出我們需要的文件,例如:*.doc
,這裡的 *
代表匹配零個或多個字符。正則表達式也是用來進行文本匹配的工具,只不過它更加強悍。引用 PHP 手冊裡的一句話:正則表達式是一個從左到右匹配目標字符串的模式,大多數字符自身就代表一個匹配 它們自身的模式。
下面給出幾個簡單例子,使對正則表達式有個初步的理解。
hi //匹配英文字符(忽略大小寫) hi , HI , Hi , hI
\bhi\b //匹配英文單詞 hi '\b'是正則裡的一特殊字符(一種斷言),表示單詞邊界
\bhi\b.*\bLucy\b //匹配如:'hi my name is Lucy' '.' 表示匹配除換行符以外的任意字符 '*' 是量詞,表示重復零次或更多次
0\d{2}-\d{8} //匹配如: 020-12345678 '\d' 匹配一個數字(0-9) '{n}' 重復n次,如{2} {8}
上面例子中的 \b
, .
, *
, \d
, {2}
都有特殊含義,在下文會有說明。
在 PHP 裡支持兩種正則分別是 POSIX 和 PCRE 。自 PHP 5.3.0起,POSIX 正則表達式擴展被廢棄。所以下文討論的都是基於 PCRE 模式。可點擊查看有關與 POSIX 正則表達式的不同和與 perl 的不同之處。
當使用 PCRE 函數 的時候,模式需要由分隔符閉合包裹。分隔符可以使任意非字母數字、非反斜線、非空白字符。經常使用的分隔符是正斜線 /
、hash符號 #
以及取反符號 ~
。下面的例子都是使用合法分隔符的模式。
/foo bar/ #^[^0-9]$# +php+ %[a-zA-Z0-9_-]%
如果分隔符需要在模式內進行匹配,它必須使用反斜線進行轉義。如果分隔符經常在模式內出現,一個更好的選擇就是是用其他分隔符來提高可讀性。例:
/http:\/\// #http://#
正則表達式的威力源於它可以在模式中擁有選擇和重復的能力。一些字符被賦予特殊的含義,使其不再單純的代表自己,模式中的這種有特殊涵義的編碼字符 稱為元字符。
共有兩種不同的元字符:一種是可以在模式中方括號外任何地方使用的,另外一種是需要在方括號內使用的。
在方括號外使用的元字符如下:
模式中方括號內的部分稱為“字符類”。 在一個字符類中僅有以下可用元字符:
示例:
\ba\w*\b
匹配以字母 a 開頭的單詞,先是某個單詞開始處 \b ,然後是字母 a ,然後是任意數量的任意單詞字符(單詞字符指的是任意字母、數字、下劃線) \w* ,最後是單詞結束處 \b 。\d+
匹配1個或更多連續的數字。^\d{5,12}$
匹配為5位到12位數字,因為使用了 ^ 和 $ ,所以輸入的整個字符串都要用來和 \d{5,12} 來匹配,也就是說整個輸入必須是5到12個數字。反斜線 \
有四種用法,詳細可點擊 轉義序列(反斜線)
【1】作為轉義字符,比如,如果你希望匹配一個 *
字符,就需要在模式中寫為 \*
。這適用於一個字符在不進行轉義會有特殊含義的情況下。 但是,對於非數字字母的字符,總是在需要其進行原文匹配的時候在它前面增加一個反斜線,來聲明它代表自己,這是安全的。如果要匹配一個反斜線,那麼在模式中使用 \\
。
反斜線在單引號字符串和雙引號字符串中都有特殊含義,因此要匹配一個反斜線, 模式中必須寫為 \\\\
。其中的原因:首先它作為字符串,反斜線會進行轉義。最後正則表達式引擎也認為反斜線是轉義。因此,需要 4 個反斜線才可以匹配一個反斜線。
【2】提供了一種對非打印字符進行可見編碼的控制手段
【3】用來描述特定的字符類
【4】一些簡單的斷言。一個斷言指定一個必須在特定位置匹配的條件,它們不會從目標字符串中消耗任何字符。反斜線斷言包括:
\b
單詞邊界\B
非單詞邊界\A
目標的開始位置(獨立於多行模式)\Z
目標的結束位置或結束處的換行符(獨立於多行模式)\z
目標的結束位置(獨立於多行模式)\G
在目標中首次匹配位置默認情況下,量詞都是”貪婪”的,也就是說,它們會在不導致模式匹配失敗的前提下,盡可能多的匹配字符(直到最大允許的匹配次數)。然而,如果一個量詞緊跟著一個 ?
標記,它就會成為懶惰(非貪婪)模式, 它不再盡可能多的匹配,而是盡可能少的匹配。
下面直接看示例,理解“貪婪”和“非貪婪”模式是怎麼回事。
對於字符串 "aa<div>test1</div>bb<div>test2</div>cc" 正則表達式 "<div>.*</div>" 匹配結果 "<div>test1</div>bb<div>test2</div>" 正則表達式 "<div>.*?</div>" 匹配結果 "<div>test1</div>"
關於更多“貪婪”和“非貪婪”模式的介紹可查閱 http://php.net/manual/zh/regexp.reference.repetition.php
PHP手冊中的描述:
左方括號開始一個字符類的描述,並以方中括號結束。單獨的一個右方括號沒有特殊含義。如果一個右方括號需要作為一個字符類中的成員,那麼可以將它寫在字符類的首字符處(如果使用了 ^ 取反,那麼是第二個)或者使用轉義符。
一個字符類在目標字符串中匹配一個單獨的字符;該字符必須是字符類中定義的字符集合的其中一個, 除非使用了 ^ 對字符類取反。如果^需要作為一個字符類的成員,確保它不是該字符類的首字符,或者對其進行轉義即可。
示例:
[aeiou] //匹配所有的小寫元音字母 [^aeiou] //匹配所有非元音字母的字符 [.?!] //匹配標點符號(.或?或!)
注意:^
只是一個通過枚舉指定那些不存在字符類之中的字符的便利符號。而不是斷言, 它仍然會從目標字符串中消耗一個字符,並且如果當前匹配點在目標字符串末尾, 匹配將會失敗。
輕松地指定一個字符范圍,范圍操作以 ASCII 整理排序。它們可以用於為字符指定數值,比如 [\000-\037]
[0-9] //代表的含意與 '\d' 就是完全一致的 [a-z0-9A-Z_] //完全等同於 '\w' 如果只考慮英文的話
下面是一個更復雜的表達式 \(?0\d{2}[) -]?\d{8}
這個表達式可以匹配幾種格式的電話號碼,像 (010)88886666,或 022-22334455 ,或 02912345678 等。
簡單分析:首先是一個轉義字符 \(
,它能出現 0 次或 1 次 ?
,然後是一個數字 0 ,後面跟著 2 個數字 \d{2}
,然後是 )
或 -
或 “空格” 中的一個,它出現 0 次或 1 次,最後是 8 個數字 \d{8}
。
豎線字符用於分離模式中的可選路徑。比如模式 gilbert|Sullivan
匹配 ”gilbert” 或者 ”sullivan”。豎線可以在模式中出現任意多個,並且允許有空的可選路徑(匹配空字符串)。匹配的處理從左到右嘗試每一個可選路徑,並且使用第一個成功匹配的。如果可選路徑在子組(下面定義)中,則”成功匹配”表示同時匹配了子模式中的分支以及主模式中的其他部分。
回看上文裡的一個例子 \(?0\d{2}[) -]?\d{8}
這個正則也能匹配 010)12345678 或 (022-87654321 這樣的 “不正確” 的格式。其實我們可以利用分支就能解決這個問題,如下:
\({1}0\d{2}\){1}[- ]?\d{8}|0\d{2}[- ]?\d{8}
這個表達式匹配 3 位區號的電話號碼,其中區號可以用小括號括起來,也可以不用,區號與本地號間可以用連字號或空格間隔,也可以沒有間隔。
使用分枝條件時,要注意各個條件的順序
正則表達式在不同的模式修飾符下匹配出的結果有可能不相同。它的語法是 :(?修飾符)
比如,(?im)
設置表明多行大小寫不敏感匹配。同樣可以用它來取消這些設置,比如 (?im-sx)
設置了 “PCRE_CASELESS”,”PCRE_MULTILINE”,但是同時取消了 “PCRE_DOTALL” 和 “PCRE_EXTENDED”。如果一個字母即出現在 -
之前, 也出現在 -
之後,這個選項被取消設置。
下面緊例舉簡單的示例,想要了解更多可點擊 內部選項設置 和 模式修飾符
示例:/ab(?i)c/
僅僅匹配 ”abc” 和 ”abC”
子組通過圓括號分隔界定,並且它們可以嵌套。
示例:
字符串:"the red king" 正則表達式:((red|white) (king|queen)) 匹配結果:array("red king", "red king", "red", "king") 描述:其中第 0 個元素是整個模式匹配的結果,後面的三個元素依次為三個子組匹配的結果。 它們的下標分別為 1, 2, 3。
經常我們會有一種需求需要使用子組進行分組,但又不需要(單獨的)捕獲它們。在子組定義的左括號後面緊跟字符串 ?:
會使得該子組不被單獨捕獲,並且不會對其後子組序號的計算產生影響。例如:
字符串:"the red king" 正則表達式:((?:red|white) (king|queen)) 匹配結果:array("red king", "red king", "king")
為了方便簡寫,如果需要在非捕獲子組開始位置設置選項, 選項字母可以位於 ?
和 :
之間,比如:
(?i:saturday|sunday) (?:(?i)saturday|sunday)
上面兩種寫法實際上是相同的模式。因為可選分支會從左到右嘗試每個分支,並且選項沒有在子模式結束前被重置,並且由於選項的設置會穿透對後面的其他分支產生影響,因此, 上面的模式都會匹配 ”SUNDAY” 以及 ”Saturday”。
再看一個匹配 IP 地址的正則 ((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
相關文章 IP地址的正則表達式
上文中涉及 PHP 正則表達式中常用的語法,有的語法沒細說和涉及到的,如:模式修飾符、後向引用、斷言、遞歸模式,等。你可以通過 PHP 手冊查看這些內容。
提示:一般而言,對於同樣的功能,正則表達式函數運行效率要低於字符串函數。如果應用程序較簡單,那麼就用字符串表達式。但是,對於可以通過單個正則表達式執行的任務來說,如果使用多個字符串函數,則是不對的。 ---- 摘自《PHP 和 MySQL Web 開放》一書。
http://php.net/manual/zh/book.pcre.php
https://msdn.microsoft.com/zh-cn/library/d9eze55x%28v=vs.80%29.aspx
http://deerchao.net/tutorials/regex/regex.htm
http://tool.chinaz.com/regex/
http://www.regexlab.com/zh/regref.htm