正則(轉)
正則對於新人來說是一個頭疼的名字,讓人聞而生畏。但是,在我看來,正則,並沒有那麼神祕,希望能通過這篇正則表示式入門教程解除正則新人對於正則的畏懼感。
接觸正則應該有三年不止了吧,我也不知道自己怎麼就走過了畏懼正則的時期,而且在一個小圈子裡面還成了正則強人。
今天就基於我對正則的理解,簡單描述一下我眼裡的正則,希望這篇正則表示式入門教程能夠解除正則新人對於正則的畏懼感。
先概括一下,正則三段論:
定錨點,去噪點,取資料
一、入門:正則字元
關於正則字元,很多文章都會講到,足足有一篇文章才能描述清楚,我這裡就不多說,對於我,平時,常用的有:
1. .
匹配不包括換行的任意字元
在php的s修飾符(單行模式)下面可以匹配換行,如$pattern='#<div>(.*?)</div>#s';
就可以匹配div內容有換行的資料。
如果需要匹配包括換行的任意字元,可以使用[\s\S]
代替.
2. \s
空格、tab、換行
[\s\S]表示匹配任意字元,\S是\s的反義。
注意區分[\s\S]與.的區別。
3. *
匹配零個或更多個,即0~n
4. +
匹配一個或更多個,即至少一個,1~n
5. \
轉義
一個特殊字元前加\
就表示轉義,說明把它當普通字元用
6. []
單字元取一個,比如[abc]會匹配a或b或c
但是,如果[]
裡面加上^
則會變成排除這個字元,如[^abc]
就表示不是a、不是b、也不是c
另外,在[]
裡面可以使用-
表示一個範圍,如[0-9]
表示從0到9,類似的還有[a-zA-Z]
,如果要包含-
字元,可以給它加上轉義[\-]
。
關於[]
常見的錯誤用法是:[ab|bc]
用來表示ab
或bc
,實際上,它得到的結果是[abc|]
,即a或b或c或|
這4個字元(單字元)的任意一個。這裡可以改成(ab|bc)
。
總結:[]裡面的特殊符有五個:[]-\^
,其他字元都是普通字元,包括*.?
等。
說明:
* ^
在[^
的首位時候才有特殊意義
* [0-9
-
在不是首尾的時候有特殊意義
* \[ \]
因為佔用[]
本身字元,所以有特殊意義
* \
本身是轉義符,有特殊意義
7. ^
字串開始
這裡的^跟[]
裡面用的^
是同一個字元,但是卻不是一個意思,這裡它表示整個字串的開始,比如^www
表示以www
開頭的字串,注意區分,不在[]
裡面的是開始符,在裡面的排除
8. $
字串結束
9. {1,3}
迴圈次數
[0-9]{1,3}
表示在0-9的範圍裡面迴圈1個、2個或者3個,可能結果有5、20、415等。
如果迴圈指定次數,如3次,則{3,3}
可以簡寫成{3}
。
如果剛好需要匹配字元{1}
,則正則需要給{
進行轉義,得到\{1}
的正則。
如果{}
中間不是數字,則{}
本身不需要轉義。
10. ?
有兩個用法
(1) 匹配一個或零個
比如https?
匹配的https
(一個s)或者http(零個s)
(2)非貪婪模式
所謂非貪婪模式,就是匹配儘可能少的內容,比如,對於源字串
<div>a</div><div>b</div>
使用<div>(.*?)</div>
會得到2個結果(注意:如果源字串有換行,使用[\s\S]替換 . ):
<div>a</div>
和
<div>b</div>
因為,當遇到第一個</div>
,非貪婪模式就不會再往後找了。
而使用<div>(.*)</div>
(貪婪模式)則會得到整個字串
<div>a</div><div>b</div>
,因為它會匹配所有字元直到後面再找不到</div>
。
更多關於?
的使用,可以參考《正則表示式匹配次數》
11. |
多個數據選一(常用於多字元)
前面提到[]
裡面的字元有選一個字元功能,但是假如不是一個字元,比如:http|ftp|svn 就需要用|
分開,|
的作用域是一直往後直到遇到括號,比如,對於源字串
http abc
ftp abc
svn abc
http|ftp|svn abc
匹配的結果是:
http
和
ftp
和
svn abc
想要匹配 http abc
和ftp abc
和svn abc
就要使用括號把前邊的協議括起來,如(http|ftp|svn) abc
可以得到預期的結果。
12. ()
資料分界和取資料
上面例子(http|ftp|svn) abc
就是資料分界的例子,然後,匹配結果會得到一個[1]
的子集資料(陣列下標1),這裡就是子模式的概念,子模式也叫分組,利用子模式,可以得到想要取出來的資料。子模式1、2、3的計算方法為左括號的計數,從左到右,從1開始,比如: (http|ftp|svn)://([^/]+)
,分組1得到的是(http|ftp|svn)
裡面的資料,分組2得到([^/]+)
裡面的資料,對於巢狀括號也是點左括號即可。在正則中有很多與括號結合的寫法,你在數左括號的時候,一定要注意,非捕獲組和環視的左括號都是不需要數的。
在使用子模式過程中,常見兩種寫法是:\1
和 $1
。
(1) \1
是在正則表示式本身中引用分組1的內容,如:
我們要匹配111
這樣的連續出現3此的數字,我們可以寫出正則:(\d)\1\1
,(\d)
匹配到第一個1
,後面再引用這個匹配內容,得到111
。
(2) $1
是在替換中呼叫分組的內容,如:
我們要替換連結引數name=Zjmainstay
為username=Zjmainstay
,我們可以使用正則name=([^&]+)
替換為username=$1
來實現,這裡的$1就引用了分組1的結果Zjmainstay
,因此得到我們想要的結果。
13. (?:)
非捕獲組
上面說到()
作為子模式可以得到它裡面的資料,但是,有些時候,()
只是作為資料分界功能,並不需要取出來,這時候就要用到非捕獲組的概念了。比如:(http|ftp|svn)://([^/]+)
只想得到域名,也就是[2]
,那麼(http|ftp|svn)
就只是資料分界的功能,這裡不需要捕獲,因此使用非捕獲組功能,(?:http|ftp|svn)
遮蔽這部分的資料獲取,此時,(?:
這個左括號排除[1]
計數,也就是(?:http|ftp|svn)://([^/]+)
中的([^/]+)
變成[1]
了。
14. 分隔符
在一些語言中,你會發現正則第一個和最後一個字元是相同的,如: /\d+/
這個/ /
在PHP中稱為分隔符,正則表示式需要由分隔符閉合包裹。在PHP中,分隔符可以使任意非字母數字、非反斜線、非空白字元。這個概念很關鍵,它能幫助我們簡化一些正則的書寫,避免錯誤,如: /<div>.*?</div>/
這個正則是錯誤的。
原因是</div>
的/
與分隔符相同,但是卻沒有做轉義。
如下程式:
preg_match('/<div>.*?</div>/', '<div>abc</div>', $match);
PHP中會收到錯誤提示:Warning: preg_match(): Unknown modifier 'd' in regexTest.php on line 2
對於這種情況,有兩種解決方案:
(1)/<div>.*?<\/div>/
(2)#<div>.*?</div>#
方案(1)是對正則內部的分隔符做轉義,方案(2)是規避了本來的/
分隔符,使用#
作為分隔符,從而避免/
需要轉義。
雖然很多情況下,都是看到前後一致的分隔符,但是,大家需要了解一下,[<div>.*?</div>]
這個表示式在PHP裡也是合法的。(不提倡使用,僅瞭解!)
15. 模式修飾符
模式修飾符在許多程式語言中都支援的,比如最常見的是i
,不區分大小寫,如javascript裡的/[a-z0-9]/i,表示匹配字母數字,不區分大小寫。
本人在寫php正則時常用的模式修飾符主要有i
和s
,如: $pattern = '#[a-z0-9]+#is';
模式修飾符s的作用主要是的.
能夠匹配換行,在處理換行資料時,通常會用到。
在PHP中,模式修飾符有兩種用法,一種是上面的,在分隔符後面的模式修飾符,它的作用範圍是全域性;另一種是在正則表示式中間的。
例如:
正則:/((?i)[A-Z]+)c/
測試字元:abcABC
匹配:abc
說明:區域性(ab)的大小寫被忽略了,(?i)的作用範圍在分組1內
如果把正則改成:/([A-Z]+)c/i
,則匹配結果將是:abcABC
示例地址:PHP正則表示式中間的模式修飾符 (選擇Version 1/2切換版本檢視結果區別)
關於PHP模式修飾符的講解,請檢視PHP手冊中的《PHP模式修飾符》。
關於常用字元的使用差不多到這裡,還有更多的請參考正則表示式30分鐘入門教程,這是我看過比較全面的正則入門資料。
二、 操作:定錨點
注:這裡的錨點區分於正則原本關於錨點的定義,此處是確定的參照文字的意思,如a標籤裡的<a
每一個正則都是有針對性的,只有這樣正則才有意義。因此,寫正則之前,先觀察你要解析的資料,找準唯一的錨點,比如,你要解析一個頁面的title標籤,得到title內容,那麼這個title就是錨點。有時候,所要取的資料確實無法定位一個唯一的錨點,那麼,你可以分解資料,先通過一個唯一錨點鎖定你的資料塊,取出來之後,再對這個資料塊取資料即可。比如,有這麼一段源字串:
<div id="module_1">
<div class="content">
content 1
</div>
</div>
<div id="module_2">
<div class="content">
content 2
</div>
</div>
你直接通過class="content"來匹配資料的話很明顯會得到兩個,那麼,你可以擴充套件它的資料域,先以id="module_1"作為錨點,獲取整個
<div id="module_1">
<div class="content">
content 1
</div>
</div>
然後在對這個資料塊的資料處理,得到class="contents"的內容即可。
因此,這裡用到2個正則:
(1)<div id="module_1">(.*?)</div>\s*<div id="module_2">
(2)<div class="content">(.*?)</div>
當然,這個正則可以改進為: <div id="module_1">\s*<div class="content">(.*?)</div>
注:為了更清晰檢視,前面原始碼做了換行,匹配失敗的朋友,可以修改 .*?
為 [\s\S]*?
修正正則。
總結:錨點,就是能唯一定位你資料的標識
三、 操作:去噪點
所謂去噪點,就是把無關的東西都當浮雲,用萬用字元過掉它,只關心我們想要的資料,比如: <meta content="text/html; charset=utf-8" http-equiv="content-type">
要從這裡得到字符集utf-8,我們需要怎麼做?
首先,定位錨點,有<meta
、charset=
和utf-8後面的"
,其他都是浮雲~
因此得到正則: <meta[^>]*?charset=([^"]+)"
即可,用子模式取資料[1]
就能得到utf-8
總結:關心的留下,不關心的都是浮雲
四、 操作:取資料
關於取資料,上面一大篇下來大家應該有概念了,就是利用子模式來獲取,這裡不再贅述。
總結:子模式計數,數左括號從1開始,排除非捕獲組的左括號
五、正則表示式高階教程
關於正則表示式的高階教程,請閱讀《深入正則表示式應用 - 正則表示式高階教程》
最後,希望大家有一個愉悅的正則之旅,你一定會愛上她的,跟我一樣。: )
轉載請附帶本文原文地址: 我眼裡的正則表示式入門教程,首發自 Zjmainstay學習筆記