原文來自我的github
0.前言
本文主要介紹了捕獲和非捕獲的概念,並舉了一些例子,這些都是正規表示式在js中進階的一些用法。後面有彩蛋哦
1.捕獲
1.1RegExp物件的相關屬性
一般用()括住的就是捕獲組,而且類似於算術中的括號,從左到右,逐層去括號。比如存在(A)((B)C)這種,他捕獲到的將會是(A)((B)C)、(B)、((B)C),並在記憶體中存放,可以通過RegExp物件的$屬性來訪問到。
/(1((2)3))/.test('123')
RegExp.$1 //123
RegExp.$2 //23
RegExp.$3 //2
複製程式碼
/(((1)2)3)/.test('123')
RegExp.$1 //123
RegExp.$2 //12
RegExp.$3 //1
複製程式碼
這個順序,按左括號的順序來算的,第幾個(
就表示第幾個$符號屬性,一般從1開始,最多$9
還有一些舊的RegExp長屬性名,在高階程式設計108頁裡面
於是,我們經常有一個這樣的需求,將一個這樣子的字串轉為陣列:
"[a,[b],c]"
,我知道很多人肯定說JSON.parse,恭喜,答對了。
然後控制檯給你的的獎勵是: Uncaught SyntaxError: Unexpected token a
在這裡轉過去的不是字串abc,而是變數abc,所以就直接報錯:Unexpected token a in JSON at position 1,想要parse,那麼這個字串應該是這樣子"['a',['b'],'c']"
,這時候,我們可以用正則把他們換掉:
"[a,[b],c]".replace(/\w+/g,"'$&'")
,
上面高程都說了$&匹配的是最近匹配的結果,我們把匹配到的字串變成被兩個引號包圍的字串,這次在parse就能正常用了。
另外,簡寫的話還是有很多不相容的問題的,最好寫全稱
1.2數字的反向引用
有的人就問,用正則怎麼匹配AABB型別的詞語?比如高高興興、亮晶晶這些。在正則裡面反斜槓+數字就可以做到,表示重複第n個捕獲組的內容,這個n和上面$後面的數字同理:
/(.)\1(.)\2/.test('高高興興') //TRUE,第一個和第二個相同,第三四個相同
/(.)(.)\2/.test('亮晶晶') // TRUE ,後面兩個相同
複製程式碼
1.3 replace
replace第二個引數還可以是一個函式,他的引數是matches,...catches,index
。即是匹配結果,捕獲組,匹配位置,準確來說,第一個引數是匹配結果,最後一個引數是匹配位置,中間所有的引數都是捕獲組。
於是對於一個常見的小需求:讓字串的連續字元變成一個
'aaaabbbbccc' =>'abc'
我們可以這樣子寫
'aaaabbbbccc' .replace(/(\w)\1+/g,function(a){
return a[0]
})
//當然還可以這樣子
'aaaabbbbccc' .replace(/(\w)\1+/g,'$1')
//還可以手動設定
'aaaabbbbccc' .replace(/(\w)\1+/g,function(a){
return a
}(1))//111
複製程式碼
2.非捕獲
以 (?) 開頭的組是非捕獲組,它不捕獲文字 ,也不針對組合計進行各種操作,不將匹配到的字元儲存到記憶體中,從而節省記憶體。也就是上面所講的$屬性他都不會具有。一般用於只需要檢測結果的情況。 (?:a)非捕獲一個a
/(?:a)1(?:b)/.test('a1b') //true
RegExp.$1 //''
複製程式碼
var reg = /(?:\d{4})-(\d{2})-(\d{2})/
var date = '2018-01-02'
reg.test(date)
RegExp.$1 // 01
RegExp.$2 // 02
複製程式碼
2.1斷言
也有人叫前瞻,顧名思義,就是往前面(右邊)看,看看是不是某個東西。
(?=x) 匹配後面是x的資料 :
/i am (?=a)/.test('i am a') //你右邊是a
(?!x) 匹配後面不是x的資料
/i am (?!a)/.test('i am b') //你右邊不是a
2.2篩選
(?!B)[A-Z]:在大寫字母集合中,除去B
/(?!B)[A-Z]/.test('A') //true
/(?!B)[A-Z]/.test('B') //false
複製程式碼
3.匹配模式
3.1惰性匹配和貪婪模式
*? 重複0次或更多次 +? 重複一次或更多次 ?? 重複0次或一次 {n,}? 重複n次或更多次 {n,m}? 重複n到m次
以上所有的匹配都是儘可能的少重複,只要滿足條件就行了,不繼續匹配了,在某個程度來說也是效能優化的方法之一。 那麼貪婪模式就是沒有做了上面的措施的都屬於貪婪模式,比如正則元字元、量詞單獨出現的情況。
對於字串'abbba'使用/ab*/g和/ab*? /g 貪婪模式:ab* 結果:abbb 和 a,第一次找到了a,繼續找發現後面接幾個b也是符合的,直到發現了第二個a才停止,再找到第二個a 惰性匹配:ab*? 結果:a 和 a,第一次找到了a,*的要求是不需要b也可以,所以停止,接著又找到第二個a
彩蛋:
檢測一個數是否是質數的方法 相信大家都見過一個很強大的函式,一行程式碼判斷出一個數是不是質數:
function isPrime(n){
return n<2?false:!/^(11+?)\1+$/.test(Array(n+1).join('1'))
}
複製程式碼
看上去好像很牛逼,容我細細道來: 首先最小的質數是2,所以先判斷是否小於2 如果大於2,先建立一個長度是n的字串,裡面鋪滿了1。Array(n+1)建立n+1個空位(undefined),再用1作為分隔符分開轉化為字串,所以就得到一個長度為n的字串,全是1組成
- ^11+?怎麼理解 表示以1開頭,後面惰性匹配多個1(1個或者無窮個)
- \1+$怎麼理解 表示重複^11+?這段匹配到的內容
- 合起來怎麼理解 神奇的地方來了,首先,惰性匹配的是一個1,也就是11,後面重複11的整數次,也就是重複2次4次6次...等等,如果剛剛好匹配到了,說明這個數能被整除,說明他不是質數。如果後面的字串不能構成2的整數倍個11,那麼第一輪惰性匹配失敗。 接著第二輪惰性匹配,匹配11,也就是前面捕獲的是111,那麼後面就開始重複111的整數倍,如果剛剛好能匹配完,說明不是質數 接著第三輪,匹配111,捕獲到1111,後面重複1111的整數倍 ... 直到不能再匹配,說明這個數就是質數。 其實,裡面相當於迴圈
for(var i = 2;i<n;i++){
if(n%i==0){return false}
}
return true
複製程式碼
正則的強大,真的是法力無邊。jQuery作者的正則,號稱世界上最強的選擇器sizzle,就是強大正則做出來的(晚點再更新sizzle解讀)