三十分鐘包會——正則表達式
正則表達式,對大家來說既熟悉又陌生。熟悉是因爲工作中有很多場景能用到,比如手機號、郵箱、密碼等規則校驗。
陌生則是因爲正則表達式看上去就是一堆亂碼,且一眼看上去很難看懂匹配規則。有時候在網上去找一個特定規則的正則表達式,搜出來的結果各不相同,執行效果更是不盡人意,想自己去修改,感覺也無從下手。
今天就花費 30 分鐘時間,帶領大家從另一個角度去剖析匹配的目的,理解匹配的思路,一步一步抽絲剝繭去學會怎麼寫正則表達式(讀正則表達式遠比寫表達式要困難)。
正則要乾的事情,可以總結爲以下靈魂三問:
Q1、匹配啥?
Q2、匹配不是啥?
Q3、匹配多少次?
Q1、匹配啥?
這個比較好理解,比如想匹配字符 a,那就直接寫/a/
,只要字符串某個位置是 a 就可以匹配上:
1/a/.test("javascript")
匹配以 a 開頭的字符串,就加上元字符^
(開始位置標識),/^a/
:
1/^a/.test("javascript")
2/^a/.test("abc")
匹配以 a 結尾的字符串,就加上元字符$
(結束位置標識),/a$/
:
1/a$/.test("javascript")
2/a$/.test("cba")
匹配字符 a 或 b,可以把對應的字符放入中括號裏/[ab]/
,只要字符串包含 a 或者 b 就可以匹配上:
1/[ab]/.test("byte")
匹配字符串 abc 或 xyz,/abc|xyz/
:
1/abc|xyz/.test("aabbxyz")
① 匹配某表達式前面的(前瞻)
exp1(?=exp2)
:匹配 exp2 前面的 exp1,匹配結果不包含 exp2
比如要匹配字符串中 script 前面的部分 java, /java(?=script)/
:
1/java(?=script)/.test("javascript,javaee,typescript")
2
3
4/java(?=script)/.exec("javascript,javaee,typescript")
5
6["java", index: 0, input: "javascript,javaee,typescript", groups: undefined]
7
② 匹配某表達式後面的(後顧)
(?<=exp2)exp1
:匹配 exp2 後面的 exp1,匹配結果不包含 exp2
比如要匹配字符串中 java 後面的部分 ee, /java(?>=ee)/
:
1/(?<=java)ee/.test("javascript,javaee,typescript")
2
3
4/(?<=java)ee/.exec("javascript,javaee,typescript")
5
6["ee", index: 15, input: "javascript,javaee,typescript", groups: undefined]
7
Q2、匹配不是啥?
匹配不是啥,意思就是取反,只要不是這些的都可以匹配,比如不想匹配字符 a,正則寫法爲/[^a]/
,是在中括號裏面加上元字符^
,這樣只要字符串滿足有不是這個集合裏面的字符,都可以被匹配上:
1/[^a]/.test("aaa")
2/[^a]/.test("abc")
匹配不是以 a 開頭的,跟之前匹配啥類似,加上元字符/^[^a]/
:
1/^[^a]/.test("javascript")
2/^[^a]/.test("abc")
匹配不是以 a 結束的,也跟之前匹配啥類似,加上元字符/[^a]$/
:
1/[^a]$/.test("javascript")
2/[^a]$/.test("cba")
不匹配字符 a、b、c,可以把對應的字符都放入中括號裏/[^abc]/
:
1/[^abc]$/.test("abccba")
① 匹配後面不是某表達式的(負前瞻)
exp1(?!exp2)
:匹配後面不是 exp2 的 exp1,匹配結果不包含 exp2
比如要匹配字符串中後面不是 script 的 java, /java(?!script)/
:
1/java(?!script)/.test("javascript,javaee,typescript")
2
3
4/java(?!script)/.exec("javascript,javaee,typescript")
5
6["java", index: 11, input: "javascript,javaee,typescript", groups: undefined]
7
② 匹配前面不是某表達式的(負後顧)
(?<!exp2)exp1
:匹配前面不是 exp2 的 exp1,匹配結果不包含 exp2
比如要匹配字符串中前面不是 java 的 script,/(?<!java)script/
:
1/(?<!java)script/.test("javascript,javaee,typescript")
2
3
4/(?<!java)script/.exec("javascript,javaee,typescript")
5
6["script", index: 22, input: "javascript,javaee,typescript", groups: undefined]
7
③ 不匹配包含 abc 的字符串
這是一個比較特殊的匹配行爲,如果只是寫成/[^abc]/
的話,這隻意味着字符串不能全是由 a、b、c 這三個組成的,跟需求不匹配。
那我們要從另外一個角度去分析,字符串的任意一個位置開始都不能連續出現 abc,我們可以利用負前瞻來實現:
- 位置後面都不能是 abc,使用負前瞻匹配空位置:
/(?!abc)/
- 從開始到結束每個位置都要覆蓋到,添加開始結束標記:
/^(?!abc)/$
- 這個位置後面可以是其他的字符,用
\w
來表示:/^(?!abc)\w$/
- 滿足上面情況後的位置,可以連續出現多個,用
+
來表示數量:/^((?!abc)\w)+$/
1/^((?!abc)\w)+$/.test("cbacbac")
2/^((?!abc)\w)+$/.test("cbacbabc")
Q3、匹配多少次?
匹配一次可以什麼都不用定義,比如匹配一個數字/\d/
,如果要匹配連續三個數字最簡單的方式就是連續寫三次:/\d\d\d/
,這樣寫本身是沒有問題的,能正確匹配。
但是如果次數太多或者次數不確定,這麼寫肯定不行,所以可以加上長度規則:
*
:匹配任意次
+
:最低匹配 1 次
?
:匹配 1 次或者 0 次
{m}
:匹配 m 次
{m,}
:最低匹配 m 次
{m,n}
:最低匹配 m 次,最多匹配 n 次,m 需要小於等於 n
正則默認是貪婪匹配,就是符合條件的會一直匹配,如果想阻止貪婪匹配,可以在長度規則後面加一個?
,比如:
1/\d{2,}/.exec("1234567890")
2
3["1234567890", index: 0, input: "1234567890", groups: undefined]
4
5
6/\d{2,}?/.exec("1234567890")
7
8["12", index: 0, input: "1234567890", groups: undefined]
① 使用分組
如果想匹配多次某個單詞如 regregregregregreg 時候怎麼辦,我們看到 reg 連續出現了 6 次,如果傻傻的把 6 個 reg 寫在了正則表達式中肯定不合適,我們就可以利用分組來實現,我們把 reg 放在括號裏面,然後讓這個分組重複 6 次,/(reg){6}/
:
1/(reg){6}/.test("regregregregregreg")
不過這樣利用分組是有個前提,就是知道要匹配的字符串就是 reg,然後重複這個分組。如果想匹配類似 8899 或者 5522 這種重疊類型的字符怎麼辦呢?那我們可以把重疊的第一個放入分組,再使用這個分組匹配下一個:
1/(\d)\1(\d)\2/.exec("2345566789")
2
3["5566", "5", "6", index: 3, input: "2345566789", groups: undefined]
② 分組捕獲
默認的分組是可以被捕獲的,上面的\1
、\2
是在正則表達式內部捕獲的分組。如果想在外部去捕獲分組匹配的數據可以使用RegExp.$1-$9
來獲取。只要正則匹配了就會有。可以使用test
、exec
或者 str 的replace
方法來獲取$1-$9
。
使用 test:
1/([a-z]{2})(\d{2})/.test("xyz123")
2RegExp.$1
3RegExp.$2
使用 replace:
1"xyz123".replace(/([a-z]{2})(\d{2})/,'$2$1')
2
③ 分組不捕獲
如果不想捕獲分組,只需要在分組內加上?:
就可以了
1/([a-z]{2})(?:\d{2})/.test("xyz123")
2RegExp.$1
3RegExp.$2
本文不去贅述元字符的含義以及組合使用方法,這些是需要記死背硬的東西。而是教給大家一種如何切入正則表達式的思維,怎麼去一步步分析要匹配的需求,把長的、複雜的需求拆成短的、簡單的,正向不行的就逆向去分析,一點一點去組合,然後迴歸靈魂三問:匹配啥?匹配不是啥?匹配多少次? ,去完成符合要求的正則表達式。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://juejin.cn/post/6939854031787393031?utm_source=gold_browser_extension