「正則表示式」全解

語言: CN / TW / HK

theme: qklhk-chocolate

我正在參加「掘金·啟航計劃」

前言

談起正則表示式每次都是去看看文件,看了就忘,大部分實際用的時候都是谷歌一下答案,那麼為了提升對正則表示式的理解,就讓我們一起通過案例再次學習吧!!!

註明:以下知識內容是對 現代 JavaScript 教程-正則篇的筆記 ,一切請以官方為準,這裡只是做了簡單的筆記。

強烈推薦!!!

除此之外,額外加了一些小知識。

正則測試網站: 連結

正則軌道圖網站:連結

修飾符

g 全域性

i 不區分大小寫

m 多行模式

s 啟用 “dotall” 模式,允許點 . 匹配換行符 \n

u 開啟完整的 Unicode 支援,該修飾符能夠正確處理代理對

y 粘滯模式,在文字中的確切位置搜尋 ```js let str = "we we W WE we dWEd dWE dwed wed ddwe" console.log( str.match(/we/gi) ) // 表示全域性搜尋 we 不區分大小寫 // /we/i/ 表示匹配第一個 we, 沒有匹配項,不會收到一個空陣列,而是會收到 null // 如果希望保底為陣列 console.log( str.match(/we/i ) || [] )

// 替換 console.log( "we we W WE we dWEd dWE dwed wed ddwe".replace(/we/i, "hone") )

// 找到至少一個匹配項則返回 true,否則返回 false /we/i.test(str) // true ``` Screen Shot 2022-10-03 at 7.51.36 PM.png

字元類

| 正則 | 含義 | | --- | --- | | /\d/ | 數字:0-9 | |/\s/ | 空格符號:包括空格,製表符 \t,換行符 \n 和其他少數稀有字元,例如 \v\f 和 \r | |/\w/ | 「單字」字元:拉丁字母或數字或下劃線 _。非拉丁字母(如西裡爾字母或印地文)不屬於 \w|

Screen Shot 2022-10-03 at 8.13.24 PM.png

let str = "+7(903)-123-45-67" console.log( str.match(/\d/) ) // 7 匹配第一個 console.log( str.match(/\d/g) ).join('') // 查詢所有,最終結果 79031234567 str.replace(/\D/g, "") // 查詢非數字並刪除,最終結果 79031234567

反向類

| 正則 | 含義 | | --- | --- | | /\D/| 除 /d 以外的任何字元 | |/\S/ | 除 /s 以外的任何字元 | |/\W/ | 除 /w 以外的任何字元 |

點(.)匹配「任何字元」

. 它與除換行符之外的任何字元匹配, 預設情況下,點與換行符 \n 不匹配

Screen Shot 2022-10-03 at 8.25.06 PM.png

寫成 /h./s, 就可以匹配任何字元

Screen Shot 2022-10-03 at 8.55.10 PM.png

對於空格,平時容易忽略 js // 以下兩種都可以匹配到 console.log("1 - 2".match(/\d - \d/)) console.log("1 - 2".match(/\d/s-\s\d/)) ⚠️注意:在正則表示式中,所有字元都很重要。

Unicode:修飾符 "u" 和類 \p{...}

Unicode 屬性 \p{…}

現代JS教程詳細連結 js // \p{L} 表示任何語言中的一個字母 let str = "A ბ ㄱ"; alert( str.match(/\p{L}/gu) ) // A,ბ,ㄱ console.log( str.match(/\p{L}/g) ); // null(沒有匹配項,因為沒有修飾符 "u") 修飾符 u 表示啟用正則表示式中對 Unicode 的支援。

  1. 4 個位元組長的字元被以正確的方式處理:被看成單個字元,而不是 2 個 2 位元組長的字元。
  2. Unicode 屬性可以被用於查詢:\p{…}

錨點:字串開始 ^ 和末尾 $

插入符號 ^ 匹配文字開頭,而美元符號 $ 則匹配文字末尾 js // /^hone/ 的意思是字串開始,緊接著就是 ^hone /^hone/.test("hone age 18") // /18$/ 是否以18結尾 /18$/.test("hone age 18")

測試完全匹配

js // 所對應的匹配項必須正好在文字 '^' 的開頭之後開始,並且結尾 '$' 必須緊跟其後 "12:24".test(/^\d\d:\d\d$/) // 可以匹配到 錨點 ^ 和 $ 屬於測試,它們的寬度為零。

什麼字串可以匹配模式 ^$? 答:空字串是唯一的匹配項:它開始並立即結束

錨點 ^ $ 的多行模式,修飾符 "m"

m 啟用多行模式修飾符 ,可以匹配每一行的^$

Screen Shot 2022-10-03 at 9.31.27 PM.png 預設情況下,錨點 ^ 僅匹配文字的開頭,在多行模式下,它匹配行的開頭

搜尋行的末尾 $

\d$ 尋找每行的最後一個數字。

查詢每行以數字開頭,中間匹配重複出現1次或者更多次數字,並且以數字結尾的匹配字元 Screen Shot 2022-10-03 at 9.34.39 PM.png

尋找新的一行

可以使用 \d\n 來尋找新的一行

Screen Shot 2022-10-03 at 9.42.34 PM.png

Screen Shot 2022-10-03 at 9.42.07 PM.png

詞邊界:\b

遇到 \b 時,它會檢查字串中的位置是否是詞邊界

Screen Shot 2022-10-03 at 9.50.05 PM.png ⚠️注意: 詞邊界 \b 不適用於非拉丁字母

轉義,特殊字元

轉義

我們想要找到一個 + 號,,要將特殊字元用作常規字元,那麼就需要在前面加上:\+

Screen Shot 2022-10-03 at 9.55.53 PM.png - 要在字面意義上搜索特殊字元 [ \ ^ $ . | ? * + ( ),我們需要在它們前面加上一個反斜槓 \(“轉義它們”)。

集合和範圍 [...]

集合

[…]  表示查詢滿足的 "任意一個" 。

Screen Shot 2022-10-03 at 10.00.47 PM.png

範圍

方括號也可以包含 字元範圍

Screen Shot 2022-10-03 at 10.03.12 PM.png

字元類是某些字元集合的簡寫: - \d —— 和 [0-9] 相同 - \w —— 和 [a-zA-Z0-9_] 相同 - \s —— 和 [\t\n\v\f\r ] 外加少量罕見的 Unicode 空格字元相同

排除範圍

[^…] "排除"範圍匹配

  • [^aeyo] —— 匹配除了 'a''e''y' 或 'o' 之外的任何字元。
  • [^0-9] —— 匹配除了數字之外的任何字元,與 \D 作用相同。
  • [^\s] —— 匹配任何非空格字元,與 \S 作用相同
  • [^] —— 表示任意字元,即對空值取反,得到全集

[…] 中的轉義

在方括號中,我們可以使用絕大多數特殊字元而無需轉義: - 符號 . + ( ) 無需轉義。 - 在開頭或結尾(未定義範圍)的連字元 - 不會被轉義。 - 插入符號 ^ 僅在開頭會被轉義(表示排除)。 - 右方括號 ] 總是會被轉義(如果我們需要尋找那個符號)

如果你為了“以防萬一”轉義了它們,這也不會有任何問題

範圍和修飾符 “u”

如果集合中有代理對(surrogate pairs),則需要標誌 u 才能使它們正常工作。 ```js console.log( '𝒳'.match(/[𝒳𝒴]/u) ) // 𝒳

// 忘記寫 u '𝒳'.match(/[𝒳-𝒴]/); // Error: Invalid regular expression ```

量詞 +, *, ? 和 {n}

{n}

在一個字元(或一個字元類,或 [...] 等)後附加一個量詞,用來指出我們具體需要的數量。

\d{5} 表示 5 位數,與 \d\d\d\d\d 相同。

確切的位數:{5}

Screen Shot 2022-10-03 at 10.15.57 PM.png

範圍:{2,5},匹配 2-5 個

Screen Shot 2022-10-03 at 10.17.05 PM.png

\d{2,} 查詢大於2的數字

Screen Shot 2022-10-03 at 10.18.45 PM.png

縮寫

+

代表 一個或多個,與 {1,} 相同。 Screen Shot 2022-10-03 at 10.20.55 PM.png Screen Shot 2022-10-03 at 10.26.42 PM.png

代表 零個或一個,與 {0,1} 相同。換句話說,它使得符號變得可選。 Screen Shot 2022-10-03 at 10.25.11 PM.png

*

代表 零個及以上,與 {0,} 相同。也就是說,字元可以出現任何次數或者不出現。 Screen Shot 2022-10-03 at 10.26.05 PM.png

貪婪量詞和惰性量詞

Screen Shot 2022-10-03 at 10.36.06 PM.png

Screen Shot 2022-10-04 at 7.39.56 PM.png

貪婪搜尋、惰性模式 具體過程

捕獲組 (...)

  • 方法 str.match 僅當不帶修飾符 g 時返回捕獲組。
  • 方法 str.matchAll 始終返回捕獲組

Screen Shot 2022-10-04 at 7.49.03 PM.png

Screen Shot 2022-10-04 at 7.49.20 PM.png

```js // 域名搜尋 "site.com my.site.com".match(/(\w+.)+\w+/g) // site.com my.site.com

// 電子郵件 "[email protected] @ [email protected]".match(/[-.\w]+@([\w-]+.)+[\w-]+/g) // [email protected], [email protected] ```

巢狀組

js let str = '<span class="my">' let regexp = /<(([a-z]+)\s*([^>]*))>/ let result = str.match(regexp) alert(result[0]) // <span class="my"> alert(result[1]) // span class="my" alert(result[2]) // span alert(result[3]) // class="my"

可選組

js 'a'.match(/a(z)?(c)?/) Screen Shot 2022-10-04 at 7.58.46 PM.png

命名組

```js // 這裡的 就是對每個陣列進行命名 let dateRegexp = /(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})/; let str = "2019-04-30";

// 匹配的組在 .groups 屬性中 let groups = str.match(dateRegexp).groups;

alert(groups.year); // 2019 alert(groups.month); // 04 alert(groups.day); // 30 ```

替換中的捕獲組

$n 中的 n 是組號 ```js let str = "John Bull"; let regexp = /(\w+) (\w+)/; // 這裡分成兩組

// $1 表示 John,$2 表示 Bull alert( str.replace(regexp, '$2, $1') ); // Bull, John

// 命名的括號 引用為 $ let regexp = /(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})/g; let str = "2019-10-30, 2020-01-01";

alert( str.replace(regexp, '$.$.$') ); // 30.10.2019, 01.01.2020 ```

非捕獲組

通過在組的開頭新增 ?: 來排除編號。 ```js let str = "Gogogo John!";

// ?: 從捕獲組中排除 'go' let regexp = /(?:go)+ (\w+)/i; let result = str.match(regexp); alert( result[0] ); // Gogogo John(完整的匹配項) alert( result[1] ); // John alert( result.length ); // 2(在陣列中沒有其他陣列項) ```

模式中的反向引用:\N\k<name>

按編號反向引用:\N

``js let str =He said: "She's the one!".`;

let regexp = /(['"])(.*?)\1/g;

alert( str.match(regexp) ); // "She's the one!" `` 程式碼執行過程:正則表示式引擎會找到第一個引號(['"])` 並記住其內容。那是第一個捕獲組。

在模式中 \1 表示“找到與第一組相同的文字”,在我們的示例中為完全相同的引號。

與此類似,\2 表示第二組的內容,\3 —— 第三分組,依此類推。

  • 注意:如果我們在捕獲組中使用 ?:,那麼我們將無法引用它。用 (?:...) 捕獲的組被排除,引擎不會記住它。
  • 注意:在替換字串中我們使用美元符號:$1,而在模式中 —— 使用反斜槓 \1

按命名反向引用:\k<name>

要引用命名的捕獲組,我們可以使用:\k<name> ``js let str =He said: "She's the one!".`; let regexp = /(?['"])(.*?)\k/g;

alert( str.match(regexp) ); // "She's the one!" ```

選擇 (OR)

用豎線 | 表示

  • gr(a|e)y 等同於 gr[ae]y
  • gra|ey 表示 gra 或 ey

前瞻斷言與後瞻斷言

前瞻斷言

x(?=y) 僅在後面是 Y 時匹配 X

js "1 turkey costs 30€".match(/\d+(?=€)/) // 30

否定的前瞻斷言

X(?!Y),意思是“搜尋 X,但前提是後面沒有 Y js "1 turkey costs 30€".match(/\d+(?!€)/) // 1

後瞻斷言

  • 肯定的後瞻斷言:(?<=Y)X,匹配 X,僅在前面是 Y 的情況下。
  • 否定的後瞻斷言:(?<!Y)X,匹配 X,僅在前面不是 Y 的情況下。 js "1 turkey costs $30".match(/(?<=\$)\d+/) // 30 "1 turkey costs $30".match(/(?<!\$)\b\d+/g)) // 1

捕獲組

js "1 turkey costs 30€".match(/\d+(?=(€))/) // 30, € "1 turkey costs $30".match(/(?<=(\$))\d+/) // 30, $

災難性回溯

現在JS教程 災難性回溯

粘性修飾符 "y",在位置處搜尋

y 修飾符讓我們能夠在源字串中的指定位置進行搜尋 js let str = 'let varName = "value"'; let regexp = /\w+/y; regexp.lastIndex = 3; alert( regexp.exec(str) ); // null(位置 3 有一個空格,不是單詞) regexp.lastIndex = 4; alert( regexp.exec(str) ); // varName(在位置 4 的單詞)

正則表示式和字串的方法

str.match(regexp)

在字串 str 中查詢 regexp 的匹配項。

  1. 如果 regexp 不帶有修飾符 g,則它以陣列的形式返回第一個匹配項,其中包含捕獲組和屬性 index(匹配項的位置)、input(輸入字串,等於 str
  2. 如果 regexp 帶有修飾符 g,則它將返回一個包含所有匹配項的陣列,但不包含捕獲組和其它詳細資訊。
  3. 如果沒有匹配項,則無論是否帶有修飾符 g,都將返回 null

str.matchAll(regexp)

它主要用來搜尋所有組的所有匹配項。

與 match 相比有 3 個區別:

  1. 它返回一個包含匹配項的可迭代物件,而不是陣列。我們可以用 Array.from 將其轉換為一個常規陣列。
  2. 每個匹配項均以一個包含捕獲組的陣列形式返回(返回格式與不帶修飾符 g 的 str.match 相同)。
  3. 如果沒有結果,則返回的是一個空的可迭代物件而不是 null

str.split(regexp|substr, limit)

alert('12, 34, 56'.split(/,\s*/)) // 陣列 ['12', '34', '56']

str.search(regexp)

方法 str.search(regexp) 返回第一個匹配項的位置,如果沒找到,則返回 -1

str.replace(str|regexp, str|func)

用於搜尋和替換的通用方法

js // 用冒號替換連字元 alert('12-34-56'.replace("-", ":")) // 12:34-56

str.replaceAll(str|regexp, str|func)

這個方法與 str.replace 本質上是一樣的,但有兩個主要的區別:

  1. 如果第一個引數是一個字串,它會替換 所有出現的 和第一個引數相同的字串​,​而 replace 只會替換 第一個
  2. 如果第一個引數是一個沒有修飾符 g 的正則表示式,則會報錯。帶有修飾符 g,它的工作方式與 replace 相同。

replaceAll 的主要用途是替換所有出現的字串。

regexp.exec(str)

regexp.exec(str) 方法返回字串 str 中的 regexp 匹配項。與以前的方法不同,它是在正則表示式而不是在字串上呼叫的。

它的行為取決於正則表示式是否具有修飾符 g

如果沒有修飾符 g,則 regexp.exec(str) 會返回與 第一個匹配項,就像 str.match(regexp) 那樣。這種行為並沒有帶來任何新的東西。

但是,如果有修飾符 g,那麼:

  • 呼叫 regexp.exec(str) 會返回第一個匹配項,並將緊隨其後的位置儲存在屬性 regexp.lastIndex 中。
  • 下一次這樣的呼叫會從位置 regexp.lastIndex 開始搜尋,返回下一個匹配項,並將其後的位置儲存在 regexp.lastIndex 中。
  • ……以此類推。
  • 如果沒有匹配項,則 regexp.exec 返回 null,並將 regexp.lastIndex 重置為 0

regexp.test(str)

方法 regexp.test(str) 查詢匹配項,然後返回 true/false 表示是否存在。