PHP Regex筆記,phpRegex筆記
什麼是Regex
在電腦上我們經常會使用(萬用字元)找出我們需要的檔案,例如:*.doc ,這裡的 * 代表匹配零個或多個字元。Regex也是用來進行文本匹配的工具,只不過它更加強悍。引用 PHP 手冊裡的一句話:Regex是一個從左至右匹配目標字串的模式,大多數字元自身就代表一個匹配 它們自身的模式。
下面給出幾個簡單例子,使對Regex有個初步的理解。
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 中正則文法
1.簡介
在 PHP 裡支援兩種正則分別是 POSIX 和 PCRE 。自 PHP 5.3.0起,POSIX Regex擴充被廢棄。所以下文討論的都是基於 PCRE 模式。可點擊查看有關與 POSIX Regex的不同和與 perl 的不同之處。
2.分隔字元
當使用 PCRE 函數 的時候,模式需要由分隔字元閉合包裹。分隔字元可以使任意非字母數字、非反斜線、非空白字元。經常使用的分隔字元是正斜線 / 、hash符號 # 以及取反符號 ~ 。下面的例子都是使用合法分隔字元的模式。
/foo bar/#^[^0-9]$#+php+%[a-zA-Z0-9_-]%
如果分隔字元需要在模式內進行匹配,它必須使用反斜線進行轉義。如果分隔字元經常在模式內出現,一個更好的選擇就是是用其他分隔字元來提高可讀性。例:
/http:\/\//#http://#
3.元字元
Regex的威力源於它可以在模式中擁有選擇和重複的能力。一些字元被賦予特殊的含義,使其不再單純的代表自己,模式中的這種有特殊涵義的編碼字元 稱為元字元。
共有兩種不同的元字元:一種是可以在模式中方括弧外任何地方使用的,另外一種是需要在方括弧內使用的。
在方括弧外使用的元字元如下:
| 代碼 |
說明 |
| / |
一般用於逸出字元 |
| ^ |
斷言目標的開始位置(或在多行模式下是行首) |
| $ |
斷言目標的結束位置(或在多行模式下是行尾) |
| . |
匹配除分行符號外的任何字元(預設) |
| [ |
開始字元類定義 |
| ] |
結束字元類定義 |
| | |
開始一個可選分支 |
| ( |
子組的開始標記 |
| ) |
子組的結束標記 |
| ? |
a:作為量詞,表示 0 次或 1 次匹配。b:位於量詞後面用於改變數詞的貪婪特性。 |
| * |
量詞,0 次或多次匹配 |
| + |
量詞,1 次或多次匹配 |
| { |
自訂量詞開始標記 |
| } |
自訂量詞結束標記 |
模式中方括弧內的部分稱為“字元類”。 在一個字元類中僅有以下可用元字元:
| 代碼 |
說明 |
| \ |
逸出字元 |
| ^ |
僅在作為第一個字元(方括弧內)時,表明字元類取反 |
| - |
標記字元範圍 |
樣本:
\ba\w*\b 匹配以字母 a 開頭的單詞,先是某個單詞開始處 \b ,然後是字母 a ,然後是任意數量的任意單詞字元(單詞字元指的是任意字母、數字、底線) \w* ,最後是單詞結束處 \b 。
\d+ 匹配1個或更多連續的數字。
^\d{5,12}$ 匹配為5位到12位元字,因為使用了 ^ 和 $ ,所以輸入的整個字串都要用來和 \d{5,12} 來匹配,也就是說整個輸入必須是5到12個數字。
4.逸出序列(反斜線)
反斜線 \ 有四種用法,詳細可點擊 逸出序列(反斜線)
【1】作為逸出字元,比如,如果你希望匹配一個 * 字元,就需要在模式中寫為 \* 。這適用於一個字元在不進行轉義會有特殊含義的情況下。 但是,對於非數字字母的字元,總是在需要其進行原文匹配的時候在它前面增加一個反斜線,來聲明它代表自己,這是安全的。如果要匹配一個反斜線,那麼在模式中使用 \\ 。
反斜線在單引號字串和雙引號字串中都有特殊含義,因此要匹配一個反斜線, 模式中必須寫為 \\\\ 。其中的原因:首先它作為字串,反斜線會進行轉義。最後Regex引擎也認為反斜線是轉義。因此,需要 4 個反斜線才可以匹配一個反斜線。
【2】提供了一種對非列印字元進行可見編碼的控制手段
【3】用來描述特定的字元類
| 代碼 |
說明 |
| \d |
任意十進位數字 |
| \D |
任意非十進位數字 |
| \h |
任意水平空白字元(since PHP 5.2.4) |
| \H |
任意非水平空白字元(since PHP 5.2.4) |
| \s |
任意空白字元 |
| \S |
任意非空白字元 |
| \v |
任意垂直空白字元(since PHP 5.2.4) |
| \V |
任意非垂直空白字元(since PHP 5.2.4) |
| \w |
任意單詞字元,單詞字元指的是任意字母、數字、底線。 |
| \W |
任意非單詞字元 |
【4】一些簡單的斷言。一個斷言指定一個必須在特定位置匹配的條件,它們不會從目標字串中消耗任何字元。反斜線斷言包括:
\b 單詞邊界
\B 非單詞邊界
\A 目標的開始位置(獨立於多行模式)
\Z 目標的結束位置或結束處的分行符號(獨立於多行模式)
\z 目標的結束位置(獨立於多行模式)
\G 在目標中最初相符位置
5.重複/量詞
| 代碼 |
說明 |
| * |
重複零次或更多次,等價於 |
| + |
重複一次或更多次,等價於 |
| ? |
重複零次或一次,等價於 |
| {n} |
重複n次 |
| {n,} |
重複n次或更多次 |
| {n,m} |
重複n到m次 |
預設情況下,量詞都是”貪婪”的,也就是說,它們會在不導致模式比對失敗的前提下,儘可能多的匹配字元(直到最大允許的匹配次數)。然而,如果一個量詞緊跟著一個 ? 標記,它就會成為懶惰(非貪婪)模式, 它不再儘可能多的匹配,而是儘可能少的匹配。
下面直接看樣本,理解“貪婪”和“非貪婪”模式是怎麼回事。
對於字串 "aatest1bbtest2cc"Regex ".*" 匹配結果 "test1bbtest2"Regex ".*?" 匹配結果 "test1"
關於更多“貪婪”和“非貪婪”模式的介紹可查閱 http://php.net/manual/zh/regexp.reference.repetition.php
6.字元類(方括弧)
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} 。
7.分支 ( | )
豎線字元用於分離模式中的可選路徑。比如模式 gilbert|Sullivan 匹配 ”gilbert” 或者 ”sullivan”。豎線可以在模式中出現任意多個,並且允許有空的可選路徑(匹配Null 字元串)。匹配的處理從左至右嘗試每一個可選路徑,並且使用第一個成功匹配的。如果可選路徑在子組(下面定義)中,則”成功匹配”表示同時匹配了子模式中的分支以及主模式中的其他部分。
回看上文裡的一個例子 \(?0\d{2}[) -]?\d{8} 這個正則也能匹配 010)12345678 或 (022-87654321 這樣的 “不正確” 的格式。其實我們可以利用分支就能解決這個問題,如下:
\({1}0\d{2}\){1}[- ]?\d{8}|0\d{2}[- ]?\d{8} 這個運算式匹配 3 位區號的電話號碼,其中區號可以用小括弧括起來,也可以不用,區號與本地號間可以用連字號或空格間隔,也可以沒有間隔。
使用分枝條件時,要注意各個條件的順序
8.內部選項設定
Regex在不同的模式修飾符下匹配出的結果有可能不相同。它的文法是 :(?修飾符)
比如,(?im) 設定表明多行大小寫不敏感匹配。同樣可以用它來取消這些設定,比如 (?im-sx) 設定了 “PCRE_CASELESS”,”PCRE_MULTILINE”,但是同時取消了 “PCRE_DOTALL” 和 “PCRE_EXTENDED”。如果一個字母即出現在 - 之前, 也出現在 - 之後,這個選項被取消設定。
下面緊例舉簡單的樣本,想要瞭解更多可點擊 內部選項設定 和 模式修飾符
樣本:/ab(?i)c/ 僅僅匹配 ”abc” 和 ”abC”
9.子組(子模式)
子組通過圓括弧分隔界定,並且它們可以嵌套。
樣本:
字串:"the red king"Regex:((red|white) (king|queen))匹配結果:array("red king", "red king", "red", "king")描述:其中第 0 個元素是整個模式比對的結果,後面的三個元素依次為三個子組匹配的結果。 它們的下標分別為 1, 2, 3。
經常我們會有一種需求需要使用子組進行分組,但又不需要(單獨的)捕獲它們。在子組定義的左括弧後面緊跟字串 ?: 會使得該子組不被單獨捕獲,並且不會對其後子組序號的計算產生影響。例如:
字串:"the red king"Regex:((?: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地址的Regex
結語
上文中涉及 PHP Regex中常用的文法,有的文法沒細說和涉及到的,如:模式修飾符、後向引用、斷言、遞迴模式,等。你可以通過 PHP 手冊查看這些內容。
提示:一般而言,對於同樣的功能,Regex函數運行效率要低於字串函數。如果應用程式較簡單,那麼就用字串運算式。但是,對於可以通過單個Regex執行的任務來說,如果使用多個字串函數,則是不對的。 ---- 摘自《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
http://www.bkjia.com/PHPjc/1091264.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/1091264.htmlTechArticlePHP Regex筆記,phpRegex筆記 什麼是Regex 在電腦上我們經常會使用(萬用字元)找出我們需要的檔案,例如: *.doc ,這裡的...