在看博客的時候,不經意間看到了一道這樣的面試題:如何使用正則表達式從URL上獲取查詢參數?html
本身思考了一下,而後嘗試用正則表達式來實現這個功能,結果發現本身對正則表達式仍是不夠熟悉;因而就順着這道題目,一步一步的去深挖這裏面所涉及到的知識點。es6
之後會不按期更新「我從xxx學到了什麼」系列,也是給本身學到的知識進行一個階段性的總結吧!面試
經過這邊文章,你能夠學到如下知識點(如下全部代碼都是基於JavaScript語言)正則表達式
URL上的查詢參數能夠直接從window.location.search
獲取,獲得的字符串就是如下形式數組
?key1=value1&key2=value2
markdown
因此題目就能夠轉換成:使用正則表達式從以上字符串獲取全部的鍵值對。函數
如下全部的匹配字符串都是 '?name=jim&age=20&hobby=basketball'oop
預期獲得的結果學習
1、name=jim
2、age=20
3、hobby=basketball
複製代碼
思路:url查詢參數部分的字符串格式是固定的:以?
開頭,後面接key=val,每組鍵值對以&
鏈接url
話很少說,思路有了,就直接上代碼
/[?&](.*)=(.*)/g
複製代碼
輸出的結果
'?name=jim&age=20&hobby=basketball'
複製代碼
竟然整個字符串都被匹配上了,問題究竟出在哪裏?
想法是匹配以?
或 &
開頭的子串,後面跟着key=val,因此寫出了 (.*)=(.*)
,這個按理應該能匹配出name=jim這樣的字符串。爲何在第一個(.*)
就把後面的整個字符串都匹配上了呢?想要的結果是第一個(.*)
匹配到 =
就結束了,也就是key,而後第二個(.*)
繼續匹配val。之因此給他們都加上()
就是想在=
前中止匹配,但實際上卻沒有按照預期獲得正確結果。
爲何會出現這種狀況呢?因而去百度瞭如下,結果發現,正則表達式默認元字符,量詞是採用貪婪模式
貪婪模式會盡量多的匹配知足條件的字符。
以下,都是會匹配最大長度字符串
.*
.+
.{1,}
.{0,}
複製代碼
正則表達式元字符,量詞默認首先最大匹配字符串,這些量詞有:+,*,?,{m,n} 。一開始匹配,就直接匹配到最長字符串。
以下 <h3>abd</h3><h3>bcd</h3>
經過正則表達式:/<h3>.*</h3>/g
進行匹配,獲得的結果爲:<h3>abd</h3><h3>bcd</h3> 也就是整個字符串都被匹配上了。
知道了正則表達式默認是貪婪模式,那麼/[?&].*=.*/g
爲何會匹配整個字符串的緣由找到了。
問題就出如今.*
,因爲它會盡量多的匹配符合條件的字符串,因此它會把整個字符串都匹配上。
這時就有一個問題:有沒有辦法讓它最小匹配呢?
答案是確定有的,可使用懶惰模式
。
和貪婪模式相反,懶惰模式是進行最小匹配。
很簡單,在表示重複字符元字符,後面加多一個?
字符便可。
上面的正則表達式若是想最小長度匹配,則能夠這樣寫:/<h3>.*?</h3>/g 那麼匹配出的結果爲:
<h3>abd</h3>
<h3>bcd</h3>
複製代碼
在一個正則表達式中,若是給子表達式加上()
,就表明這部分是一個分組,整個表達式是第一個分組。
舉個栗子
/[?&](.*)=(.*)/g
複製代碼
其中(.*)
就是一個分組。
通常分組有兩個做用:一、在結果中獲取特定的分組匹配結果;二、在表達式中進行引用
在結果中獲取特定的分組匹配結果
執行regex.exce(str)
時,獲得的結果是一個數組;以下
假如要匹配一個年份的年月日,一個簡單的正則能夠是這樣的
/(\d{4})-(\d{2})-(\d{2})/g
複製代碼
匹配字符串1993-01-01
獲得的結果以下
[
"1993-01-01",
"1993",
"01",
"01"
]
複製代碼
第一個是整個表達式的匹配結果,第二個是第一個分組的匹配結果,依次類推。
那麼問題來了,我怎麼知道某個分組是第幾個?
分組能夠經過從左到右計算其開括號來編號。整個表達式始終是第一個分組。
以下表達式 (A)(B(C)) 有四個分組 0 (A)(B(C)) 1 (A) 2 (B(C)) 3 (C) 在執行exec函數時,返回的結果也是按照上面的分組返回結果。 第一個返回結果是0號分組匹配到的字符串;第二個返回結果是1號分組匹配到的字符串;依次類推
在表達式中進行引用
在一些場景中,須要匹配和前面某個子表達式匹配結果同樣的結果,好比說,須要匹配重複的單詞,那麼就可使用分組引用。
一樣舉個栗子,匹配重複的單詞
abc abc
能夠經過如下表達式進行匹配
/\b(\w+)\b\s+\1\b/
複製代碼
\1
:引用第一個分組匹配到的結果,上面的栗子就是引用(\w+)
獲得結果,也就是abc
須要注意的是,引用分組進行匹配,僅僅是引用,它並非真正的分組,因此在結果的數組中是不會有這個引用匹配到的結果。
以上修飾符,若是有不理解的,能夠去ES入門教程-正則表達式學習下,這裏就再也不贅述了。
不熟悉的能夠去菜鳥教程學習下,這裏就再也不贅述了。
通過初版的慘痛失敗,意識到了要使用懶惰模式進行匹配,因此改進如下獲得第二版表達式
[?&]?(.*)?=(.*)?
複製代碼
輸出結果
1、?name=
2、jim&age=
3、20&hobby=
複製代碼
一共匹配到了三個,但結果仍是不是我預期的,
問題在哪裏呢?
[?&]?:匹配上的字符串以?或&或都沒有開頭
.*?:匹配任意字符串,匹配數可使0或者1,也就是說能夠是什麼都不匹配
.*?=.*?
:第一個.*?
會匹配到=前的字符串,而第二個.*
能夠什麼都不匹配,由於它是懶惰模式
並且從預期的結果來看,在匹配結果中,並不須要?&
。僅僅是想匹配?或&後面的ke=val。
這時候,後向斷言就能夠發揮做用了。
這個名字讀起來就很彆扭,不用去理解它的具體含義,權當是一個名字就行。
語法
(?<=exp)x
複製代碼
斷言部分在()內,它的做用是匹配知足x的字符串時,校驗x前面的字符串是否知足exp,若是知足,則匹配,不然不匹配。
舉個具體的栗子
只匹配在#後面的數字
const reg = /(?<=#)\d+/g
const str = '#90abc'
str.match(reg)
// [90]
複製代碼
語法
(?<!=exp)x
複製代碼
它的做用是匹配知足x的字符串時,校驗x前面的字符串是否知足exp,若是不知足,則匹配,不然不匹配。
舉個具體栗子
不匹配#後面的數字
const reg = /(?<!#)\d+/g
const str = '#90abc$100edf%102ooo'
str.match(reg)
// [0, 100, 102]
複製代碼
語法
x(?=exp)
複製代碼
它的做用是匹配知足x的字符串時,校驗x後面的字符串是否知足exp,若是知足,則匹配,不然不匹配。
舉個具體栗子
只匹配百分號前的數字
const reg = /d+(?=%)/
const str = '90%'
str.match(reg)
// [90]
複製代碼
語法
x(?!exp)
複製代碼
它的做用是匹配知足x的字符串時,校驗x後面的字符串是否知足exp,若是不知足,則匹配,不然匹配。
舉個具體栗子
只匹配不在百分號前的數字
const reg = /d+(?!%)/g
const str = '100$'
str.match(reg)
// [100]
複製代碼
又多了一件厲害的裝備,如今是時候祭出最終版的表達式了
/(?<=[?&])[^&]*/g
複製代碼
輸出結果
1、name=jim
2、age=20
3、hobby=basketball
複製代碼
(?<=[?&])
:後向斷言,匹配後面的表達式以後,再次進行驗證,若是都知足,則匹配
[^&]*
:匹配不是&的任意字符
?