正則表達式完整指南
正則表達式是一種更爲強大的字符串匹配、字符串查找、字符串替換等操作工具。今天來學習一下 JavaScript 中的正則表達式!
一、基本概念
正則表達式(Regular Expression,在代碼中常簡寫爲 regex、regexp 或 RE)使用單個字符串來描述、匹配一系列符合某個句法規則的字符串搜索模式。搜索模式可用於文本搜索和文本替換。它用一系列字符定義搜索模式。
正則表達式的用途有很多,比如:
-
表單輸入驗證;
-
搜索和替換;
-
過濾大量文本文件(如日誌)中的信息;
-
讀取配置文件;
-
網頁抓取;
-
處理具有一致語法的文本文件,例如 CSV。
正則表達式的語法如下:
/正則表達式主體/修飾符(可選)
先來看一個最基本的正則表達式:/處/
,它只匹配到了字符串中的第一個 “處”:
這裏,正則表達式的主體就是 “處”,沒有使用修飾符,我們會在後面來介紹正則表達式的修飾符。
二、創建方式
創建正則表達式的方式有兩種:
- 字面量: 正則表達式直接放在
/ /
之中:
const rex = /pattern/;
- 構造函數: RegExp 對象表示正則表達式的一個實例:
const rex = new RegExp("pattern");
這兩種方法的一大區別是對象的構造函數允許傳遞帶引號的表達式,通過這種方式就可以動態創建正則表達式。
通過這兩種方法創建出來的 Regex 對象都具有相同的方法和屬性:
let RegExp1 = /a|b/
let RegExp2 = new RegExp('a|b')
console.log(RegExp1) // 輸出結果:/a|b/
console.log(RegExp2) // 輸出結果:/a|b/
三、模式匹配
關於正則表達式最複雜的地方就是如何編寫正則規則了,下面就來看如何編寫正則表達式。
1. 字符集合
如果我們想匹配 bat、cat 和 fat 這種類型的字符串該怎麼辦?可以通過使用字符集合來做到這一點,用 [] 表示,它會匹配包含的任意一個字符。這裏就可以使用/[bcf]at/ig
:
可以看到,這裏匹配到了字符串中的 bat、cat、fat。因爲我們使用了 g 修飾符,所以匹配到了三個結果。
當然,字符集也可以用來匹配數字:
2. 字符範圍
如果我們想要在字符串中匹配所有以 at 結尾的單詞,最直接的方式是使用字符集,並在其中提供所有的字母。對於這種在一個範圍中的字符, 就可以直接定義字符範圍,用-
表示。它用來匹配指定範圍內的任意字符。這裏就可以使用/[a-z]at/ig
:
可以看到,正則表達式按照我們的預期匹配了。
常見的使用範圍的方式如下:
-
部分範圍:
[a-f]
,匹配 a 到 f 的任意字符; -
小寫範圍:
[a-z]
,匹配 a 到 z 的任意字符; -
大寫範圍:
[A-Z]
,匹配 A 到 Z 的任意字符; -
數字範圍:
[0-9]
,匹配 0 到 9 的任意字符; -
符號範圍:
[#$%&@]
; -
混合範圍:
[a-zA-Z0-9]
,匹配所有數字、大小寫字母中的任意字符。
3. 數量字符
如果想要匹配三個字母的單詞,根據上面我們學到的字符範圍,可以這樣來寫:
[a-z][a-z][a-z]
這裏我們匹配的三個字母的單詞,那如果想要匹配 10 個、20 個字母的單詞呢?難道要一個個來寫範圍嗎?有一種更好的方法就是使用花括號{}
來表示,來看例子:
可以看到,這裏我們匹配到了所有連續 5 個字母的單詞(包括超過 5 個字母的單詞,不過只會匹配到前 5 個字母)。
其實匹配重複字符的完整語法是這樣的:{m,n}
,它會匹配前面一個字符至少 m 次至多 n 次重複,{m} 表示匹配 m 次,{m,} 表示至少 m 次。
所以,當我們給 5 後面加上逗號時,就表示至少匹配五次:
所以這裏就匹配到了所有連續 5 個或 5 個以上的單詞。
當匹配次數爲至少 4 次,至多 5 次時,匹配結果如下:
除了可以使用大括號來匹配一定數量的字符,還有三個相關的模式:
-
+
:匹配前面一個表達式一次或者多次,相當於{1,}
; -
*
:匹配前面一個表達式 0 次或者多次,相當於{0,}
; -
?
:單獨使用匹配前面一個表達式零次或者一次,相當於{0,1}
,如果跟在量詞 *、+、?、{} 後面的時候將會使量詞變爲非貪婪模式(儘量匹配少的字符),默認是使用貪婪模式。
來看一個簡單的例子,這裏我們匹配的正則表達式爲/a+/ig
,結果如下:
它和/a{1,}/ig
的匹配結果是一樣的:
使用/[a-z]+/ig
就可以匹配任意長度的純字母單詞:
4. 元字符
使用元字符可以編寫更緊湊的正則表達式模式。常見的元字符如下:
-
\d
:相當於[0-9]
,匹配任意數字; -
\D
:相當於[^0-9]
; -
\w
:相當於[0-9a-zA-Z]
,匹配任意數字、大小寫字母和下劃線; -
\W
:相當於:[^0-9a-zA-Z]; -
\s
:相當於[\t\v\n\r\f]
,匹配任意空白符,包括空格,水平製表符\t
,垂直製表符\v
,換行符\n
,回車符\r
,換頁符\f
; -
\S
:相當於[^\t\v\n\r\f]
,表示非空白符。
來看一個簡單的例子:
這裏使用\d
來匹配任意數字、字母和下劃線。這裏就匹配到了 7 個連續四位的字符。
5. 特殊字符
使用特殊字符可以編寫更高級的模式表達式,常見的特殊字符如下:
-
.
:匹配除了換行符之外的任何單個字符; -
\
:將下一個字符標記爲特殊字符、或原義字符、或向後引用、或八進制轉義符; -
|
:邏輯或操作符; -
[^]
:取非,匹配未包含的任意字符。
來看一個簡單的例子,如果我們使用 /ab*/ig
進行匹配,結果就如下:
那我們就是想要匹配 * 怎麼辦?就可以使用 \
對其進行轉義:
這樣就只會匹配到 ab*
了。
或匹配也很簡單,來看例子,匹配規則爲:/ab|cd/ig
,匹配結果如下:
這裏就會匹配到字符串中所有 ab
和 cd
字符。那如果想要匹配 sabz
或者scdz
呢?開頭和結尾是相同的,只有中間的兩個字符是可選的。其實只需要給中間的或部分加上括號就可以了:
取非規則在範圍中使用,來看例子:
這裏匹配到了所有非字母的字符。
6. 位置匹配
如果我們想匹配字符串中以某些字符結尾的單詞,以某些字符開頭的單詞該如何實現呢?正則表達式中提供了方法通過位置來匹配字符:
-
\b
:匹配一個單詞邊界,也就是指單詞和空格間的位置; -
\B
:匹配非單詞邊界; -
^
:匹配開頭,在多行匹配中匹配行開頭; -
$
:匹配結尾,在多行匹配中匹配行結尾; -
(?=p)
:匹配 p 前面的位置; -
(?!=p)
:匹配不是 p 前面的位置。
最常見的就是匹配開始和結束位置。先來看一個開始位置的匹配,這裏使用 /^ex/igm
來匹配多行中以ex
開頭的行:
使用/e$/igm
來匹配以 e 結尾的行:
可以使用 \w+$
來匹配每一行的最後一個單詞:
需要注意,這裏我們都使用 m
修飾符開啓了多行模式。
使用 /(?=the)/ig
來匹配字符串中the
前的面的位置:
我們可以使用\b
來匹配單詞的邊界,匹配的結果如下:
這可能比較難理解,我們可以使用以下正則表達式來匹配完整的單詞:\b\w+\b
,匹配結果如下:
四、修飾符
正則表達式常見的修飾符如下:
-
g
:表示全局模式,即運用於所有字符串; -
i
:表示不區分大小寫,即匹配時忽略字符串的大小寫; -
m
:表示多行模式,強制 $ 和 ^ 分別匹配每個換行符。
這些修飾符總是用在最後一個正斜槓後面,可以一起使用。下面來分別看看這些修飾符的作用。
最開始的例子中,字符串中有兩個 “處”,但是隻匹配到了一個。這是因爲正則表達式默認匹配第一個符合條件的字符。如果想要匹配所有符合條件的字符,就可以使用 g
修飾符:
/處/g
這樣就匹配到了所有符合條件的字符:
當需要匹配引英文字符串,並且忽略字符串的字母大小寫時,i
修飾符就派上用場了。先來看下面的表達式:
/a/g
在進行匹配時,它匹配到了字符串中所有的 a
字符。但是最開始的 A
是沒匹配到的,因爲兩者大小寫不一致:
那我們來添加上 i
修飾符:
/a/gi
這時所有的 a
都被匹配到了,無論是大寫還是小寫,總共匹配到了三個 a
:
還有一個小疑問, 如果是對象構造函數的方式來構造正則表達式使,如何添加這些修飾符呢?其實很簡單,只要將修飾符作爲第二個參數傳遞給 構造函數就可以了:
let regExp = new RegExp('[2b|^2b]', 'gi')
console.log(regExp) // 輸出結果:/[2b|^2b]/gi
五、RegExp 實例
1. 實例方法
RegExp 實例置了test()
和exec()
這兩個方法來校驗正則表達式。下面來分別看一下這兩個方法。
(1)test()test()
用於檢測一個字符串是否匹配某個模式,如果字符串中含有匹配的文本,則返回 true,否則返回 false。
const regex1 = /a/ig;
const regex2 = /hello/ig;
const str = "Action speak louder than words";
console.log(regex1.test(str)); // true
console.log(regex2.test(str)); // false
(2)exec()exec()
用於檢索字符串中的正則表達式的匹配。該函數返回一個數組,其中存放匹配的結果。如果未找到匹配,則返回值爲 null。
const regex1 = /a/ig;
const regex2 = /hello/ig;
const str = "Action speak louder than words";
console.log(regex1.exec(str)); // ['A', index: 0, input: 'Action speak louder than words', groups: undefined]
console.log(regex2.exec(str)); // null
在當在全局正則表達式中使用 exec
時,每隔一次就會返回null
,如圖:
這是怎麼回事呢?MDN 的解釋如下:
在設置了 global 或 sticky 標誌位的情況下(如 /foo/g or /foo/y),JavaScript RegExp 對象是有狀態的。他們會將上次成功匹配後的位置記錄在 lastIndex 屬性中。使用此特性,exec() 可用來對單個字符串中的多次匹配結果進行逐條的遍歷(包括捕獲到的匹配),而相比之下, String.prototype.match() 只會返回匹配到的結果。
爲了解決這個問題,我們可以在運行每個 exec 命令之前將lastIndex
賦值爲 0:
2. 實例屬性
RegExp 實例還內置了一些屬性,這些屬性可以獲知一個正則表達式的各方面的信息,但是用處不大。
六、字符串方法
在 JavaScript 中有 6 種常用的方法是支持正則表達式的,下面來分別看看這些方法。
1. search()
search()
方法用於檢索字符串中指定的子字符串,或檢索與正則表達式相匹配的子字符串,並返回子串的起始位置。如果沒有找到任何匹配的子串,則返回 -1。
const regex1 = /a/ig;
const regex2 = /p/ig;
const regex3 = /m/ig;
const str = "Action speak louder than words";
console.log(str.search(regex1)); // 輸出結果:0
console.log(str.search(regex2)); // 輸出結果:8
console.log(str.search(regex3)); // 輸出結果:-1
可以看到,search()
方法只會返回匹配到的第一個字符的索引值,當沒有匹配到相應的值時,就會返回 - 1。
2. match()
match()
方法可在字符串內檢索指定的值,或找到一個或多個正則表達式的匹配。如果沒有找到任何匹配的文本, match()
將返回 null
。否則,它將返回一個數組,其中存放了與它找到的匹配文本有關的信息。
const regex1 = /a/ig;
const regex2 = /a/i;
const regex3 = /m/ig;
const str = "Action speak louder than words";
console.log(str.match(regex1)); // 輸出結果:['A', 'a', 'a']
console.log(str.match(regex2)); // 輸出結果:['A', index: 0, input: 'Action speak louder than words', groups: undefined]
console.log(str.match(regex3)); // 輸出結果:null
可以看到,當沒有 g
修飾符時,就只能在字符串中執行一次匹配,如果想要匹配所有符合條件的值,就需要添加 g
修飾符。
3. matchAll()
matchAll()
方法返回一個包含所有匹配正則表達式的結果及分組捕獲組的迭代器。因爲返回的是遍歷器,所以通常使用for...of
循環取出。
for (const match of 'abcabc'.matchAll(/a/g)) {
console.log(match)
}
//["a", index: 0, input: "abcabc", groups: undefined]
//["a", index: 3, input: "abcabc", groups: undefined]
需要注意,該方法的第一個參數是一個正則表達式對象,如果傳的參數不是一個正則表達式對象,則會隱式地使用 new RegExp(obj)
將其轉換爲一個 RegExp
。另外,RegExp 必須是設置了全局模式g
的形式,否則會拋出異常 TypeError
。
4. replace()
replace()
用於在字符串中用一些字符串替換另一些字符串,或替換一個與正則表達式匹配的子串。
const regex = /A/g;
const str = "Action speak louder than words";
console.log(str.replace(regex, 'a')); // 輸出結果:action speak louder than words
可以看到,第一個參數中的正則表達式匹配到了字符串的第一個大寫的 A,並將其替換爲了第二個參數中的小寫的 a。
5. replaceAll()
replaceAll()
方法用於在字符串中用一些字符替換另一些字符,或替換一個與正則表達式匹配的子串,該函數會替換所有匹配到的子字符串。
const regex = /a/g;
const str = "Action speak louder than words";
console.log(str.replaceAll(regex, 'A')); // 輸出結果:Action speAk louder thAn words
需要注意,當使用一個 regex
時,您必須設置全局("g")標誌, 否則,它將引發 TypeError
:"必須使用全局 RegExp 調用 replaceAll"。
6. split()
split()
方法用於把一個字符串分割成字符串數組。其第一個參數是一個字符串或正則表達式,從該參數指定的地方分割字符串。
const regex = / /gi;
const str = "Action speak louder than words";
console.log(str.split(regex)); // 輸出結果:['Action', 'speak', 'louder', 'than', 'words']
這裏的 regex
用來匹配空字符串,所以最終在字符串的每個空格處將字符串拆成了數組。
七、實際應用
下面來通過正則表達式的幾個實際應用來鞏固一下上面的知識。
1. 匹配密碼
檢查密碼的格式,其包含至少一個大寫字母、小寫字母、數字、符號,長度爲 8-12 位:
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*\W).{8,12}$/g
這裏我們主要使用了正則表達式中的正向前瞻,正向前瞻語法爲(?=pattern)
,即在目標字符串的相應位置必須有pattern
部分匹配的內容,但不作爲匹配結果處理,更不會存儲在緩衝區內供以後使用。來看一下這個正則表達式的每一部分的含義:
-
(?=.*[a-z])
:匹配任何後面跟着小寫字母的字符; -
(?=.*[A-Z])
:匹配任何後面跟着大寫字母的字符; -
(?=.*\d)
:匹配任何後面跟着數字的字符; -
(?=.*\W)
:匹配任何後面跟着符號的字符; -
.{8,12}
:匹配的長度至少爲 8 個字符,至多爲 12 個字符。 -
^
和$
可以保證匹配從字符串的開頭到結尾進行匹配,也就是隻對整個密碼進行匹配,不考慮部分匹配。
下面是測試結果:
2. 匹配郵箱
檢查電子郵箱的地址:
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[ a-zA-Z0-9-]+)*$/g
下面來看一下這個正則表達式每一部分的含義:
-
^[a-zA-Z0-9.!#$%&'*+/=?^_
~-]+:檢查是否使用了所有有效字符並且至少有了一個(末尾的
+` 用於檢查是否至少有一個字符); -
[a-zA-Z0-9-]+
:這一部分用來檢驗主機名是否有效,主機名可以是大小寫字母、數字、中橫線。最後的 + 表示至少有一位; -
(?:\.[a-zA-Z0-9-]+)*
:這一部分是可選的域名後綴,這裏使用的*
就表示前面的字符是 0 個或者多個,這樣. com、.com.cn 等域名都可以匹配到; -
^
和$
可以保證匹配從字符串的開頭到結尾進行匹配,也就是隻對整個郵箱字符串進行匹配,不考慮部分匹配。
下面是測試結果:
3. 匹配數字
檢查數字是否是整數:/^\d+$/
, 其中\d+
表示至少有一位數字。測試結果如下:
檢查數字是否是小數:/^\d*\.\d+$/
,其中\d*
表示至少有 0 位數字,\.
就是把小數點進行了轉義操作,\d+
就表示至少有一位小數位。測試結果如下:
校驗一個數字是不是一個金額:/^\d+(.\d{2})?$/
八、實用工具
1. Regex101
Regex101 是學習正則表達式最有效的工具網站之一。在 REGULAR EXPRESSION 欄中可以輸入正則表達式,可以在輸入框右側選擇需要的修飾符,在下面的 TEST STRING 欄中輸入要測試的字符串,即可顯示出匹配到的結果。在右側的 EXPLANATION 區域會顯示出對輸入的正則表達式的詳細解釋。右下角的 QUICK REFERENCE 欄會顯示正則表達式速查表。
Regex101 還支持在上面練習編寫正則表達式:
可以在上面搜索一些正則表達式的庫:
除此之外,我們還可以使用 RegexDebugger 來跟蹤匹配的過程。更多功能可以在 Regex101 上進行探索。
官網: https://regex101.com/
2. RegExr
RegExr 是一個基於 JavaScript 開發的在線工具,用來創建、測試和學習正則表達式。它是一個開源的工具,具有以下特性:
-
輸入時,結果會實時更新;
-
支持 JavaScript 和 PHP/PCRE RegEx;
-
將匹配項或表達式移至詳細信息;
-
保存並與他人共享表達式;
-
使用工具探索結果;
-
瀏覽參考以獲取幫助和示例;
-
在編輯器中使用 cmd-Z/Y 撤消和重做。
官網: https://regexr.com/
3. Regex Pal
Regexpal 是一個基於 Javascript 的在線正則表達式驗證工具。它的頁面非常簡潔,只有兩個輸入框,上面的輸入框中可以輸入正則表達式(匹配規則),下面的輸入框可以輸入待匹配的數據。此外,根據具體要求,還可以設置忽略大小寫、多行匹配等參數。
官網: https://www.regexpal.com/
4. Regex-Vis
Regex-Vis 是一個輔助學習、編寫和驗證正則的工具。它不僅能對正則進行可視化展示,而且提供可視編輯正則的能力。在輸入一個正則表達式後,會生成它的可視化圖形。然後可以點選或框選圖形中的單個或多個節點,再在右側操作面板對其進行操作,具體操作取決於節點的類型,比如在其右側插入空節點、爲節點編組、爲節點增加量詞等。
官網: https://regex-vis.com/
5. Regex previewer
Regex previewer 是一個 VScode 插件,在插件市場搜索名稱即可安裝。當我們在編寫正則表達式時,可以直接使用快捷鍵 Ctrl+Alt+M (windows)或者 ⌥+⌘+M
(Mac)在編輯器右側啓動一個標籤頁,我們可以在這個標籤頁寫一寫測試用例,用來測試我們寫的正則表達式,寫完字符串用例之後,點擊我們編寫的正則表達式上方的 Test Regex... 即可,這樣右側匹配到字符就會高亮顯示了,如下圖:
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/7L4m90r-17cka3yLHsKZIg