[JS高程] 字串模式匹配方法

jaycethanks發表於2021-11-25

1. RegExp 物件

JS 中像其他語言一樣,有正規表示式的支援,即RegExp物件。

該物件主要提供了兩個物件方法,分別是:

  1. exec()
  2. test()

用法示例如下:

let str = "cat bat";
let exp = /.at/g;
exp.exec(str); // ['cat', index: 0, input: 'cat bat', groups: undefined]
exp.exec(str); // ['bat', index: 4, input: 'cat bat', groups: undefined]
exp.exec(str); // null
let str = "cat bat";
let exp = /.at/g;
exp.test(str); // true

更多參看這裡 link

2. 字串模式匹配方法

但是在處理字串時,以上方法並不常用,取而代之,String 型別專門為在字串中實現模式匹配設計了幾個方法。

  1. match();
  2. search();
  3. replace();

其中, match()search() 方法的使用較為類似。 二者的使用示例如下:

let str = "cat, bat, sat, hat";
let pt = /.at/g;
let result = str.match(pt);
console.log(result);
/*
[
    "cat",
    "bat",
    "sat",
    "hat"
]
*/
let str = "cat, bat, sat, hat";
let pt = /.at/g;
let result = str.search(pt);
console.log(result); // 0

這兩個方法都是接受一個引數, 是一個 正規表示式字串,或者RegExp 物件。

所不同的是, match() 返回被匹配的項, 如果是/g 匹配模式,則返回一組包含所有匹配項的陣列,否則,返回第一個匹配的到的子串, 其返回解僱結果和exec() 的執行結果相同。 而search() 的返回結果則是首個匹配項的索引值,如果沒有匹配項則返回-1

2.2 replace()

重點的需要熟悉replace() 方法, 這是最常用的方法

為了簡化子字串替換操作,ECMAScript 提供了 replace() 方法。 這個方法接收兩個引數,第一個引數可以是一個RegExp物件,或者一個字串(這個字串不會轉換為正規表示式),第二個引數可以是一個字串或者一個函式。

也就是,repalce() 方法可以單純的用於替換字串中的子串,也可以用以替換匹配模式匹配的目標子串。

  1. 替換子字串,示例:

    let str = "cat, bat, sat, hat";
    let result = str.replace("at","hello");// chello, bat, sat, hat
    

    ⚠️ 注意: 如果第一個引數是字串,那麼只會替換第一個子字串。要是想替換所有的子字串,第一個引數必須為正規表示式並且開啟了全域性匹配模式。

  2. 替換正則匹配項,示例:

    let str = "cat, bat, sat, hat";
    let pt = /at/g;
    let result = str.replace(pt,"hello");
    console.log(result);// chello, bhello, shello, hhello
    

2.2.1 第二個引數為字串的應用情況

第二個引數是字串的情況下,有幾個特殊的字元序列,可以用來插入正規表示式操作的值。

字元序列 替換文字
$$ $
$& 匹配整個模式的子字串。等同RegExp.lastMatch
$' 匹配的子字串之前的字串。 —— RegExp.rightContext
$` 匹配的子字串之後的字串。 —— RegExp。leftContext
$n 匹配第 n 個捕獲組的子字串, n ( 0~9 ), 若沒有捕獲組,值為空串。
$nn 匹配第 nn 個捕獲組, nn (01~99), 若沒有捕獲組,值為空串。

以下示例說明:

let str = "I love the moment you smile";
let exp = /I (love (the moment(you)) smile/

該例項中,將會有三個捕獲組:

image-20211124085813888

$$

str.replace(exp,"$$"); // 將匹配到的子串替換為 `$` 符號
// '$'

⚠️ 注意: 儘管是存在捕獲組,但是因為整個模式就能匹配完整的源字串, 還是直接全部被替換為了 $ 符號。

$&

str.replace(exp,"$&"); // 'I love the moment you smile'

$'$`

str.replace(exp,"$'"); // ''
str.replace(exp,"$`"); // ''
// "I love the moment you smile "為首個完整匹配,其左側,右側都是空字元

$n$nn

str.replace(exp,"$1"); //'love the moment you'
str.replace(exp,"$2"); //'the moment'
str.replace(exp,"$3"); //'you'

注意:

  1. 以上執行的含義是, 將第二個引數中的字串,替換掉源字串中被第一個引數(pattern)所匹配命中的子字串。

  2. 以上的示例中,$', $` 的輸出都是空串,以及$$ 直接返回$的原因是, $& 作為整個pattern 命中結果,已經和源字串相同了,即整個完整的字串被命中。 如果做以下修改,結果將不同:

    let str = 'I love the moment you smile';let exp = /love (the moment (you))/;str.replace(exp,"$$")// 'I $ smile'str.replace(exp,"$'")// 'I  smile smile'str.replace(exp,"$`")// 'I I  smile'
    

所以, 一點小結: 當字串方法replace() 的第二個引數為字串時, replace() 方法的替換目標是 $&

以上述示例來描述,就是 字串 "I love the moment you smile" 的原始值包裝物件提供的replace() 方法, 在通過正規表示式/love (the moment (you))/ 來進行內容替換時, 將會以整個pattern(表示式)匹配到的子串為目標,即 “love the moment you” 為替換目標,也就是$& 。 並無關於pattern 中是否有捕獲組。

2.2.2 第二個引數為函式的應用情況

根據是否有捕獲組,表現不同

replace() 方法第二個引數還支援函式, 目的是用於更加靈活的替換規則,擴充捕獲組的使用需求等。

該函式根據第一個引數中(pattern) 是否具有捕獲組,函式的傳遞引數也不同:

  1. 沒有捕獲組時 : 函式收到3個引數 :①. 與整個模式匹配的字串 ②. 匹配項在字串中的開始位置 ③. 整個字串
  2. 有捕獲組時 : 每個匹配捕獲組的字串都會作為引數傳給這個函式,但是最後兩個引數,依舊是 整個匹配模式開始的位置 和 原始字串。 因此引數的個數時不確定的,為n + 2

以下是一些示例:

示例1 :沒有捕獲組:

image-20211124094627930

示例2 : 只有一個捕獲組:

image-20211124095242291

示例3 : 有多個捕獲組:

image-20211124095925750

第二個引數為函式時的字串替換示例:

示例1:

function htmlEscape(text) { 
  return text.replace(/[<>"&]/g, function(match, pos, originalText) { 
    switch(match) {  
      case "<": 
        return "&lt;"; 
      case ">": 
        return "&gt;"; 
      case "&": 
        return "&amp;"; 
      case "\"": 
        return "&quot;"; 
    } 
  }); 
} 
 
console.log(htmlEscape("<p class=\"greeting\">Hello world!</p>")); 
// "&lt;p class=&quot;greeting&quot;&gt;Hello world!</p>" 

❓ ​這個地方存在一個疑問:

不知道當Pattern 中含有捕獲組的時候要怎麼去處理, 例如:

let str = "i always love the way you lie";
let exp = /always (love) the way (you) lie/;
let res = str.replace(exp, function (...args) {
  for (let i = 0; i < args.length; i++) {
    if (args[i] === "love") {
      return "hate";
    } else if (args[i] === "you") {
      return "he";
    }
  }
});
console.log(res); //i hate

期望是將源字串中的 "love"->"hate", "you" -> "she" 。
https://stackoverflow.com/questions/70105383/how-to-use-while-replace-methods-second-parameter-is-a-function-and-the-sam

相關文章