一、元字符
正則表達式中的特殊字符被稱作元字符,常用的元字符如下:
. 點,匹配除換行符以外的任意字符
\w 匹配數字、字母和下划線(可以匹配漢字)
\s 匹配空白字符,如空格、換行符、制表符等
\d 匹配數字
\b 匹配單詞的開始或結束
^ 匹配字符串的開始
$ 匹配字符串的結尾
\(特殊) 轉義字符。如果需要匹配的內容含有元字符本身的,需要使用轉義字符進行轉義
“.”的用法(使用python3.x進行舉例,具體的python3.x的正則表達式的用法請參考http://www.cnblogs.com/cdinc/p/5789429.html):
string='qq14717287xx@qq.com&10010/10086' pattern=r'.com.' item=re.search(pattern,string) print(item.group())
運行結果是:
.com&
可以看到匹配的結果是com和前一個字符和后一個字符,這個就是這個“.”的作用。
“\w”的用法:
string='qq14717287xx@qq.com&10010/10086' pattern=r'7287\w' item=re.search(pattern,string) print(item.group())
運行結果是:
7287x
可以看到匹配的結果是“7287”后面再多加一個字母,這就是“\w”的用法。
“\s”的用法:
string='qq14717287xx@qq. com&10010/10086' pattern=r'\scom' item=re.search(pattern,string) print(item.group())
運行結果是:
com
在com前面又匹配到了一個空格
“\d”的用法:
string='qq14717287xx@qq. com&10010/10086' pattern=r'qq\d' item=re.search(pattern,string) print(item.group())
運行結果是:
qq1
“\b”的用法:
string='qq14717287xx@qq. com&10010/10086' pattern=r'\bcom\b' item=re.search(pattern,string) print(item.group())
運行結果是:
com
將“com”作為一個單詞進行匹配。因為匹配的是單詞的開頭或者結尾,所以如果匹配的是“om\b”也可以匹配上,是com單詞的結尾,但是“\bom”則無法匹配,因為om前面有c,認為com是一個單詞,“om”並不是單詞的開始位置,所以無法匹配。
“^”的用法:
string='qq14717287xx@qq. com&10010/10086' pattern=r'^qq' item=re.search(pattern,string) print(item.group())
運行結果是:
匹配以“qq”開頭,所以能夠匹配上,如果是“^com”,則無法匹配上。
“$”的用法:
string='qq14717287xx@qq. com&10010/10086' pattern=r'0086$' item=re.search(pattern,string) print(item.group())
運行結果是:
0086
匹配以0086結尾,能夠匹配上。填寫正則表達式的時候,$符號要放在匹配字符串或者公式的后面。
“\”(轉移字符)的用法:
string='qq14717287xx@qq.com$10010/10086' pattern=r'com\$' item=re.search(pattern,string) print(item.group())
運行結果是:
com$
可見本次就是純粹的查找的“com$”,而沒有將$作為元字符處理。
二、重復
正則表達式中提供了幾種重復的方式,畢竟如果匹配三個字母使用“\w\w\w”的方式太不方便了,如果是三個還好,如果是十個、二十個呢。
* 重復零次或者多次
+ 重復一次或者多次
? 重復零次或者一次
{n} 重復n次
{n,} 重復n次或者更多次
{n,m} 重復n次到m次
“*”的用法:
string='qq14717287xx@qq.com$10010/10086' pattern=r'\w*' item=re.search(pattern,string) print(item.group())
運行結果是:
qq14717287xx
可以看到匹配到了多次字母和數字。
“+”的用法:
string='qq14717287xx@qq.com$10010/10086' pattern=r'\w+!*' item=re.search(pattern,string) print(item.group())
運行結果是:
qq14717287xx
結果是匹配到了多個字母、數字,因為匹配字符串中沒有“!”。如果匹配的是“\w+!+”,則無法匹配。這從另一個方面說明了“*”和“+”的區別。
“?”的用法:
string='qq14717287xx@qq.com$10010/10086' pattern=r'\w+@?' item=re.findall(pattern,string) print(item)
運行結果是:
['qq14717287xx@', 'qq', 'com', '10010', '10086']
可以看到,使用匹配的字符后面含有1個或沒有“@”符號
“{n}”的用法:
string='qq14717287xx@qq.com$10010/10086' pattern=r'\w{5}' item=re.findall(pattern,string) print(item)
運行結果是:
['qq147', '17287', '10010', '10086']
對於數字、字母重復了五次的地方進行了匹配。
“{n,}”的用法:
string='qq14717287xx@qq.com$10010/10086' pattern=r'\w{5,}' item=re.findall(pattern,string) print(item)
運行結果是:
['qq14717287xx', '10010', '10086']
匹配了數字、字母重復5次及以上的地方。
“{n,m}”的用法:
string='qq14717287xx@qq.com$10010/10086' pattern=r'\w{5,8}' item=re.findall(pattern,string) print(item)
運行結果是:
['qq147172', '10010', '10086']
可以看到,因為限定了重復次數(5-8次),所以第一個組合(qq14717287xx)被分開進行匹配。
三、特定字符查找
1、單字查找
查找字母、數字我們已經知道了,但是如果我們只是想查找特定字符怎么辦?比如我只想找到有沒有aeiou這幾個字母怎么辦?沒關系,那就把他們都列出來就好了,寫作[aeiou]
string='qq14717287xx@qq.com$10010/a10086' pattern=r'.{4}[aeiou].{2}' item=re.findall(pattern,string) print(item)
運行結果是:
['qq.com$', '010/a10']
可以看到,匹配了“o”和“a”前面的四個字符和后面的兩個字符。
列在[]中的字符只匹配是否含有其中的字符,而[]中的寫法也有很大的自由度。
比如想匹配數字,可以不用寫為[0123456789],而是寫為[0-9],這兩個的含義是相同的。
同理,小寫字母可以寫為[a-z],大寫字母可以寫為[A-Z],匹配英文可以寫為[a-zA-Z]
string='qq14717287XX@qq.com$10010/a10086' pattern=r'[0-9]{5}' item=re.findall(pattern,string) print('匹配數字:',item) pattern=r'[a-zA-Z]{2}' item=re.findall(pattern,string) print('匹配字母:',item) pattern=r'[a-zA-Z0-9]{4}' item=re.findall(pattern,string) print('匹配數字和字母:',item)
運行結果是:
匹配數字: ['14717', '10010', '10086'] 匹配字母: ['qq', 'XX', 'qq', 'co'] 匹配數字和字母: ['qq14', '7172', '87XX', '1001', 'a100']
2、分組查找
如果我們想重復多個字符應該怎么辦呢?我們可以使用小括號來指定子表達式(也叫做分組),我們也可以指定分組重復的次數。比如我想匹配一個IP地址,我們可以這樣寫:
string="This computer's IP is 192.168.0.1" pattern=r'(\d{1,3}\.){3}\d{1,3}' item=re.search(pattern,string) print(item.group())
運行結果是:
192.168.0.1
於是我們就得到了一個IP地址(有效性不進行驗證,驗證方法比較復雜,但是網上也有,可以百度之)。
3、反義查找
我們已經知道幾種查找的元字符了,但是如果我們就是不想要查找那幾種字符怎么辦呢。沒關系,正則表達式還提供反義查找方式。
\W 匹配任意非\w的字符
\S 匹配任意非\s的字符
\D 匹配任意非\d的字符
\B 匹配非單詞開頭或結尾的部分
[^q] 匹配非q字符的部分
[^aeiou] 匹配非aeiou的部分
“\W”的用法:
string='qq14717287xx@qq.com&10010/10086' pattern=r'\W+.{0,3}' item=re.findall(pattern,string) print(item)
運行結果是:
['@qq.', '&100', '/100']
“\S”的用法:
string='qq14717287 xx@qq. com&10010/10086' pattern=r'\S+' item=re.findall(pattern,string) print(item)
運行結果是:
['qq14717287', 'xx@qq.', 'com&10010/10086']
“\D”的用法:
string='qq14717287xx@qq.com&10010/10086' pattern=r'\D+' item=re.findall(pattern,string) print(item)
運行結果是:
['qq', 'xx@qq.com&', '/']
“\B”的用法:
string='qq14717287xx@qq.com&10010/10086' pattern=r'.{2}\B00\B.{2}' item=re.findall(pattern,string) print(item)
運行結果是:
['&10010', '/10086']
“^”的用法:
string='qq14717287xx@qq.com&10010/10086' pattern=r'[^7]+' item=re.findall(pattern,string) print(item)
運行結果是:
['qq14', '1', '28', 'xx@qq.com&10010/10086']
匹配非7的部分。
四、分支條件
正則表達式也支持類似於OR的方式,使用“|”符號連接幾個表達式,只要其中一個表達式匹配上就算匹配。
比如我們有幾個電話,既有010-12345678的固定電話方式,又有15812345678的手機方式,我們應該怎么匹配呢?
string1='010-12345678' string2='15812345678' pattern=r'\d{3}-\d{8}|\d{11}' item=re.findall(pattern,string1) print(item) item=re.findall(pattern,string2) print(item)
運行結果:
['010-12345678'] ['15812345678']
可以看到,我們使用相同匹配表達式都能夠匹配到需要的內容。
但是,需要注意的是,這種方式的匹配是有順序的。比如美國的郵政編碼是一個5位數字或者是一個用連字符連起來的9位數字,那么它的匹配表達式的寫法是:\d{5}-\d{4}|\d{5},如果順序顛倒,那么就只會匹配5個數字而不會再匹配用連字符連起來的9位數字了,因為只要能夠匹配上就不會再考慮后面的表達式了。所以使用分枝條件的時候需要注意條件的順序。
五、注釋
在正則表達式中,我們可以使用(?#content)來使用注釋。
string='qq14717287xx@qq.com&10010/10086' pattern=r'\D+(?#This is a comment)' item=re.findall(pattern,string) print(item)
運行結果是:
['qq', 'xx@qq.com&', '/']
可以看到,注釋內容並沒有影響匹配結果。
我們還可以將正則表達式寫為多行,通過設置忽略多余的空白字符選項,可以將注釋寫為只是#開頭,這種方式下,#的后面至行末都被判斷為注釋。
string='qq14717287xx@qq.com&10010/10086' pattern=r'''\D+ #This is a comment .{2} #This is a comment too \d+ #This is also a comment''' item=re.findall(pattern,string,re.X) print(item)
運行結果是:
['qq14717287', 'xx@qq.com&10010', '/10086']
六、貪婪匹配與懶惰匹配
正則表達式默認匹配盡可能多的字符。
string='this is a demo text!' pattern=r't.*t' item=re.findall(pattern,string) print(item)
運行結果是:
['this is a demo text']
這個表達式匹配了最長的以t開始中間沒有或多個字符再以t結尾的一個字段。這種匹配模式就被稱為貪婪模式。
但是有時候我們的需求是需要匹配盡可能少的字符,這種方式在正則表達式中被成為懶惰模式。需要匹配懶惰模式,只需要在限定符后面加入一個?就可以了。
懶惰模式有以下幾種形式:
*? 重復任意次,但是匹配盡可能少的字符。
+? 重復1次或者多次,但是匹配盡可能少的字符。
?? 重復0次或者1次,但是匹配盡可能少的字符。
{n,m}? 重復n次到m次,但是匹配盡可能少的字符。
{n,}? 重復n次以上,但是匹配盡可能少的字符。
以下看一下它們的用法及匹配結果:
“*?”:重復任意次,但是匹配盡可能少的字符。
string='this is a demo text!' pattern=r't.*?t' item=re.findall(pattern,string) print(item)
運行結果是:
['this is a demo t']
這樣就就匹配了t和t之間最少的字符。but,為什么匹配結果不是“text”呢?因為正則表達式有一個優先級更高的規則,最先開始的匹配擁有最高優先權。
“+?”:重復1次或者多次,但是匹配盡可能少的字符。
string='this is a demo text,too!' pattern=r'd.+o' item=re.findall(pattern,string) print('普通(貪婪)匹配結果:',item) pattern=r'd.+?o' item=re.findall(pattern,string) print('懶惰匹配結果:',item)
運行結果是:
普通(貪婪)匹配結果: ['demo text,too'] 懶惰匹配結果: ['demo']
“??”:重復0次或者1次,但是匹配盡可能少的字符。
string='this isi a demo text,too!' pattern=r't.?o' item=re.findall(pattern,string) print('普通(貪婪)匹配結果:',item) pattern=r't.??o' item=re.findall(pattern,string) print('懶惰匹配結果:',item)
運行結果是:
普通(貪婪)匹配結果: ['too'] 懶惰匹配結果: ['to']
“{n,m}?”:重復n次到m次,但是匹配盡可能少的字符。
string='this isi a demo text,too!' pattern=r't.{2,5}t' item=re.findall(pattern,string) print('普通(貪婪)匹配結果:',item) pattern=r't.{2,5}?t' item=re.findall(pattern,string) print('懶惰匹配結果:',item)
運行結果是:
普通(貪婪)匹配結果: ['text,t'] 懶惰匹配結果: ['text']
“{n,}?”重復n次以上,但是匹配盡可能少的字符。
string='this isi a demo text,too!' pattern=r't.{1,}i' item=re.findall(pattern,string) print('普通(貪婪)匹配結果:',item) pattern=r't.{1,}?i' item=re.findall(pattern,string) print('懶惰匹配結果:',item)
運行結果是:
普通(貪婪)匹配結果: ['this isi'] 懶惰匹配結果: ['thi']
七、分組
有時我們給定的匹配條件很多,但是需要的卻不多。比如這個字符串“isi 1234 isi apple isi”,加入我們只想要“isi”中間的內容該怎么匹配呢?如果只用之前給的方法沒有辦法取出來,這就要說道分組了。
分組就是將整個正則表達式分成不同的組,只獲取不同組匹配的內容。
其中分組使用“()”來區分,括號內匹配的內容為一個組。
示例如下:
string='qq14717287xx@qq123456.com&10010/10086' pattern=r'(qq\d+).*?(qq\d+)' item=re.match(pattern,string) print('總共匹配到%s個數據。'%len(item.groups())) for i in range(len(item.groups())+1): print('第%d個數據是:%s'%(i,item.group(i)))
運行結果是:
總共匹配到2個數據。
第0個數據是:qq14717287xx@qq123456
第1個數據是:qq14717287
第2個數據是:qq123456
可以看到,默認分組都是按照號碼自動分配。
但是明明匹配到了兩個數據,為什么有三個顯示呢?這是因為分組中,分組0表示全部正則表達式,所以第0個數據就是'qq\d+.*?qq\d+'的匹配結果。
有時候我們就是這么耿直,我們不想用你自動分配的號碼,我想自己給匹配的字符串命名。這也能夠實現,只要使用“(?P<name>pattern)”的方式就可以了。(以下為python3.x的實現方式,其他語言可能會有差別,但是原理相似)。
string='qq14717287xx@qq123456.com&10010/10086' pattern=r'(?P<aaa>qq\d+).*?(?P<bbb>qq\d+)' item=re.match(pattern,string) print(item.group("aaa")) print(item.group('bbb'))
運行結果是:
qq14717287
qq123456
但是有時我們又不想給某一個分組分配名字,連系統默認分配的號碼都不想給他,也能夠實現(至今沒有發現這種方式存在的意義-_-|||)。
string='qq14717287xx@qq123456.com&10010/10086' pattern=r'(?P<aaa>qq\d+).*?(?:qq\d+)' item=re.match(pattern,string) print('總共匹配到%s個數據。'%len(item.groups())) for i in range(len(item.groups())+1): print('第%d個數據是:%s'%(i,item.group(i)))
運行結果是:
總共匹配到1個數據。
第0個數據是:qq14717287xx@qq123456
第1個數據是:qq14717287
可以看到,這次我們並沒有匹配到第2個內容
所以,分組的情況分為以下幾種:
(pattern) 匹配pattern,系統自動為組分配號碼
(?P<name>pattern) 匹配pattern,並將組命名為name
(?:pattern) 匹配pattern,不捕獲匹配的文本,也不分配號碼
八、后向引用
后向引用可以用來匹配與某個分組相同的部分,提供查找重復字符組的方便的方法。
我們可以通過示例來進行了解。
string='qq14717287xx@qq14723456.com&10010/10086' pattern=r'(qq\d+).*?(\1)' item=re.match(pattern,string) print('總共匹配到%s個數據。'%len(item.groups())) for i in range(len(item.groups())+1): print('第%d個數據是:%s'%(i,item.group(i)))
運行結果是:
總共匹配到2個數據。
第0個數據是:qq14717287xx@qq147
第1個數據是:qq147
第2個數據是:qq147
其中\1表示第一個分組的內容,兩個分組相同並且符合匹配方式的部分的是"qq147",所以都匹配到了"qq147"。
如果我們將第二個字符串換為qq123456之后會發生什么呢?
string='qq14717287xx@qq123456.com&10010/10086' pattern=r'(qq\d+).*?(\1)' item=re.match(pattern,string) # item=re.findall(pattern,string) print('總共匹配到%s個數據。'%len(item.groups())) for i in range(len(item.groups())+1): print('第%d個數據是:%s'%(i,item.group(i)))
運行結果是:
總共匹配到2個數據。
第0個數據是:qq14717287xx@qq1
第1個數據是:qq1
第2個數據是:qq1
兩個分組相同並且符合匹配方式的部分的只有"qq1",所以只匹配到了"qq1"。
如果已經給分組進行了命名呢?也沒有問題,只需要將代表分組號碼的"\1"換為分組名稱"?P=aaa"即可。
string='qq14717287xx@qq14723456.com&10010/10086' pattern=r'(?P<aaa>qq\d+).*?(?P=aaa)' item=re.match(pattern,string) print('總共匹配到%s個數據。'%len(item.groups())) for i in range(len(item.groups())+1): print('第%d個數據是:%s'%(i,item.group(i)))
運行結果是:
總共匹配到1個數據。
第0個數據是:qq14717287xx@qq1
第1個數據是:qq1
可以看到和之前的匹配結果相同,但是第二個分組並沒有進行捕獲,真不知道是不是一個好消息。
九、零寬斷言
我們想要使用一些匹配規則作為位置,只匹配前面或者后面的內容,那應該怎么實現呢?這就要說道斷言了,斷言指定了一個位置。
我們還是通過例子來看吧。
(?=pattern)它斷言自身出現的位置的前面能匹配需要匹配的內容
string='qq14717287xx@qq123456.com&10010/10086' pattern='\d+(?=\D+)' item=re.findall(pattern,string) print(item)
運行結果是:
['14717287', '123456', '10010']
可以看到,我們找到了不是數字的字符前面的數字。需要注意的是,斷言本身並不是匹配結果。
(?<=pattern)它斷言自身出現的位置的后面能匹配需要匹配的內容
string='qq14717287xx@qq123456.com&10010/10086' pattern='(?<=\d)\D+' item=re.findall(pattern,string) print(item)
運行結果是:
['xx@qq', '.com&', '/']
注意,這種方式下,\d后不能加*、+、?,因為這種方式不支持不定長度正則表達式(如果有錯誤,歡迎指正)。
(?=pattern)它斷言需要匹配的內容后面不跟隨pattern
string='qq14717287xx@qq123456.com&10010/10086' pattern='\d+(?!\D)' item=re.findall(pattern,string) print(item)
運行結果是:
['1471728', '12345', '1001', '10086']
(?<!pattern)它斷言需要匹配的內容前面不跟隨pattern,同樣的,這種方式下,pattern后不能加*、+、?
string='qq14717287xx@qq123456.com&10010/10086' pattern='(?<!\D)\d+' item=re.findall(pattern,string) print(item)
運行結果是:
['4717287', '23456', '0010', '0086']
十、沒有提到的東西
雖然已經進行整理,但是肯定還有各種沒有提到的東西。
為什么沒有提到呢?因為我沒有試出來用法啊(尷尬笑.jpg),以后如果能夠試出來怎么使用再繼續更新吧。
如果有什么疑問或者建議請留言,或者發送我的郵箱(就是那個1471728764.qq.com啦),我會揀我會的回答的。