關於JS的正規表示式

lhyt發表於2018-04-24

原文來自我的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頁裡面

111

於是,我們經常有一個這樣的需求,將一個這樣子的字串轉為陣列: "[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解讀)

相關文章