0.寫在前面
在上一篇文章中,我們學習了正則的一些基礎元字符,相信大家都已經忘卻的差不多了,可以點擊上面的鏈接再溫習下。
今天我們一起來學習下正則中量詞的三種匹配模式,貪婪模式、非貪婪模式、獨占模式,這些模式會改變正則中量詞的匹配行為,是每次貪婪的匹配到更多呢,還是不貪婪見好就收呢,如果不了解這些,我們寫出的正則很可能是錯誤的,甚至會引發嚴重的線上性能問題。
1.量詞
本篇文章所講的內容和量詞關系比較密切,先回顧下:
我們還可以用 {m,n} 的方式來表示 * + ? 這3種元字符:
元字符 | 同義表示方法 | 示例 |
---|---|---|
* | {0,} |
ab* 可以匹配 a 或者 abb |
+ | {1,} |
ab+ 可以匹配 ab 或者 abb 但不能匹配 a |
? | {0,1} |
ab? 可以匹配 a 或者 ab 但不能匹配 abb |
2.貪婪模式前傳
在正則中,表示次數的量詞默認是貪婪的,在貪婪模式下,會盡可能最大長度的去匹配目標字符串,我們用正則 a+ 和 a* 來匹配字符串 aaabb 測試一下。
2.1 使用 a+ 進行匹配
可以看到只匹配到了1個結果 aaa
對應的 python 代碼如下:
1
2
3
4
5
|
import re print (re.findall(r 'a+' , 'aaabb' )) 輸出:[ 'aaa' ] |
2.2 使用 a* 進行匹配
可以看到匹配到了4個結果,其中還有3個是空字符串
對應的 python 代碼如下:
1
2
3
4
5
|
import re print (re.findall(r 'a*' , 'aaabb' )) 輸出:[ 'aaa' , ' ', ' ', ' '] |
為什么會匹配到空字符串呢?因為星號(*)代表匹配0到多次,匹配0次就是空字符串,那前面還有個 aaa 呢,為什么 aaa 之間的空字符串沒有被匹配到?
這就引入到了我們今天要講的,貪婪模式與非貪婪模式,從字面上很好理解,貪婪模式就是盡可能多的匹配,非貪婪模式就是盡可能少的匹配。
3.貪婪模式
一起來分析下上面正則 a* 的匹配過程:
字符串 | a | a | a | b | b | 空字符串 |
---|---|---|---|---|---|---|
下標 | 0 | 1 | 2 | 3 | 4 | 5 |
匹配 | 開始 | 結束 | 說明 | 匹配內容 |
---|---|---|---|---|
第一次 | 0 | 3 | 到第一個字母b發現不匹配,輸出aaa | aaa |
第二次 | 3 | 3 | 匹配剩下的bb,發現匹配不上,輸出空字符串 | 空字符串 |
第三次 | 4 | 4 | 匹配剩下的b,發現匹配不上,輸出空字符串 | 空字符串 |
第四次 | 5 | 5 | 匹配剩下的空字符串,輸出空字符串 | 空字符串 |
a* 在匹配字符串 aaabb 時,會盡可能多的把前面的 a 都匹配上,直到第一個字母 b 不滿足要求為止,匹配上3個 a,后面每次匹配的都是空字符串。
看到這里,相信你已經對貪婪模式有了更深的印象,貪婪模式的特點就是盡可能進行最大長度匹配,就是有多少要多少,下面我們在一起來看下與它完全相反的匹配模式。
4.非貪婪模式
上面講完了貪婪模式,貪婪模式是盡可能最大長度匹配,非貪婪模式就是盡可能最小長度匹配,在量詞的后面加一個問號(?),就成了非貪婪模式,比如 a*?
對應的 python 代碼如下:
1
2
3
4
5
6
7
8
9
10
11
|
import re / / 貪婪匹配 print (re.findall(r 'a*' , 'aaabb' )) 輸出:[ 'aaa' , ' ', ' ', ' '] / / 非貪婪匹配 print (re.findall(r 'a*?' , 'aaabb' )) 輸出:[' ', ' a ', ' ', ' a ', ' ', ' a ', ' ', ' ', ' '] |
學完了貪婪模式與非貪婪模式,你可能會問,我什么情況下會用到呢,下面舉個栗子感受下:
需求是查找一段字符串中,所有雙引號括起來的內容,上面使用貪婪匹配與非貪婪匹配的對比,差別很明顯對吧。
5.獨占模式
不管是貪婪模式,還是非貪婪模式,匹配過程中都需要發生回溯才能完成想要的功能,但是在有一些場景,我們不需要回溯,匹配不上直接返回失敗就可以了,因此正則匹配中還有另外一種模式,獨占模式,它和貪婪模式很像,但匹配過程中不會發生回溯,在一些使用場景中性能會更好。
先來講講什么是回溯,再舉個栗子,有一個正則表達式和目標字符串,我們分別看下在三種匹配模式下都發生了什么:
5.1 貪婪匹配過程
正則表達式:ab{1,3}c
目標字符串:abbc
在匹配時,b{1,3} 會盡可能長的去匹配目標字符串,匹配完 abb 之后,因為要盡可能長的匹配(3個 b),目標字符串中的c就會匹配不上,這個時候會發生向前回溯,吐出當前字符 c,用正則中的 c 去匹配,匹配成功。
1
2
3
4
5
|
import regex print (regex.findall(r 'ab{1,3}c' , 'abbc' )) 輸出:[ 'abbc' ] |
5.2 非貪婪匹配過程
正則表達式:ab{1,3}?c
目標字符串:abbc
在匹配時,b{1,3} 會盡可能短的去匹配目標字符串,匹配完 ab 之后,會直接用正則 c 去匹配目標字符串剩下的 b,匹配不上,發生向前回溯,重新用正則 b{1,3} 匹配 目標字符串剩下的 b,然后正則 c 匹配 目標字符串剩下的 c,匹配成功。
1
2
3
4
5
|
import regex print (regex.findall(r 'ab{1,3}?c' , 'abbc' )) 輸出:[ 'abbc' ] |
5.3 獨占匹配過程
在量詞后面加上 + 就是獨占模式。
正則表達式:ab{1,2}+bc
目標字符串:abbc
在匹配時,b{1,2} 會盡可能長的去匹配目標字符串,匹配完 abb 之后,會用正則 b 匹配目標字符串剩下的 c,匹配不上,不回溯,匹配失敗。
1
2
3
4
5
|
import regex print (regex.findall(r 'ab{1,2}+bc' , 'abbc' )) 輸出:[] |
6.寫在最后
最后在總結下上面講到的內容:
到這里,正則表達式的量詞與貪婪就講完了,如果有問題可以給我留言評論,謝謝。
正則表達式在線校驗工具:https://regex101.com/
到此這篇關于正則表達式量詞與貪婪的使用詳解的文章就介紹到這了,更多相關正則表達式 量詞與貪婪內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/kong_gu_you_lan/article/details/118565947