分組和反向引用

分組將多個模式作為一個整體進行分組,而捕獲組在使用正則表示式模式匹配字串時提供額外的子匹配資訊。反向引用引用同一正則表示式中先前捕獲的組。

試一試

// Groups
const imageDescription = "This image has a resolution of 1440×900 pixels.";
const regexpSize = /(\d+)×(\d+)/;
const match = imageDescription.match(regexpSize);
console.log(`Width: ${match[1]} / Height: ${match[2]}.`);
// Expected output: "Width: 1440 / Height: 900."

// Backreferences
const findDuplicates = "foo foo bar";
const regex = /\b(\w+)\s+\1\b/g;
console.log(findDuplicates.match(regex));
// Expected output: Array ["foo foo"]

型別

字元 含義
(x)

捕獲組: 匹配 x 並記住匹配。例如,/(foo)/ 匹配並記住 "foo" 在 "foo bar" 中。

一個正則表示式可能包含多個捕獲組。在結果中,捕獲組的匹配通常在一個數組中,其成員的順序與捕獲組中左括號的順序相同。這通常就是捕獲組本身的順序。當捕獲組巢狀時,這變得很重要。可以使用結果元素的索引 ([1], …, [n]) 或預定義 RegExp 物件的屬性 ($1, …, $9) 來訪問匹配。

捕獲組會帶來效能開銷。如果您不需要記住匹配的子字串,請優先使用非捕獲括號(見下文)。

如果設定了 /.../g 標誌,String.prototype.match() 不會返回組。但是,您仍然可以使用 String.prototype.matchAll() 來獲取所有匹配。

(?<Name>x)

命名捕獲組: 匹配 "x" 並將其儲存在返回匹配的 groups 屬性中,名稱由 <Name> 指定。組名需要尖括號 (<>)。

例如,要從電話號碼中提取美國區號,我們可以使用 /\((?<area>\d\d\d)\)/。結果數字將出現在 matches.groups.area 下。

(?:x)

非捕獲組: 匹配 "x" 但不記住匹配。無法從結果陣列的元素 ([1], …, [n]) 或預定義 RegExp 物件的屬性 ($1, …, $9) 中召回匹配的子字串。

(?flags:x), (?:flags-flags:x)

修飾符: 僅對封閉模式啟用或停用指定標誌。修飾符中只能使用 ims 標誌。

\n

反向引用: 其中 "n" 是一個正整數。匹配正則表示式中第 n 個捕獲組(從左括號開始計數)匹配的相同子字串。例如,/apple(,)\sorange\1/ 匹配 "apple, orange, cherry, peach" 中的 "apple, orange,"。

\k<Name>

命名反向引用: 反向引用到由 <Name> 指定的命名捕獲組最後匹配的子字串。

例如,/(?<title>\w+), yes \k<title>/ 匹配 "Do you copy? Sir, yes Sir!" 中的 "Sir, yes Sir"。

注意: 這裡的 \k 字面上表示命名捕獲組反向引用的開始。

示例

使用分組

在此示例中,我們透過使用捕獲組來記住兩個單詞,從而匹配結構化格式中的兩個單詞。 \w+ 匹配一個或多個單詞字元,括號 () 建立一個捕獲組。 g 標誌用於匹配所有出現。

js
const personList = `First_Name: John, Last_Name: Doe
First_Name: Jane, Last_Name: Smith`;

const regexpNames = /First_Name: (\w+), Last_Name: (\w+)/g;
for (const match of personList.matchAll(regexpNames)) {
  console.log(`Hello ${match[1]} ${match[2]}`);
}

捕獲組參考中檢視更多示例。

使用命名組

此示例與上面相同,但我們使用命名捕獲組來記住匹配的單詞。這樣,我們可以透過它們的含義訪問匹配的單詞。

js
const personList = `First_Name: John, Last_Name: Doe
First_Name: Jane, Last_Name: Smith`;

const regexpNames =
  /First_Name: (?<firstName>\w+), Last_Name: (?<lastName>\w+)/g;
for (const match of personList.matchAll(regexpNames)) {
  console.log(`Hello ${match.groups.firstName} ${match.groups.lastName}`);
}

命名捕獲組參考中檢視更多示例。

使用分組和反向引用

在此示例中,我們首先使用 ['"] 匹配單引號或雙引號字元,記住它,然後使用 .*? 匹配任意數量的字元 (*? 是一個非貪婪量詞),直到我們再次使用 \1 匹配記住的引號字元。 \1 是對第一個捕獲組的反向引用,它匹配相同型別的引號。因此,結果將是兩個字串: "'"'"'

js
const quote = `Single quote "'" and double quote '"'`;
const regexpQuotes = /(['"]).*?\1/g;
for (const match of quote.matchAll(regexpQuotes)) {
  console.log(match[0]);
}

反向引用參考中檢視更多示例。

使用分組和匹配索引

透過提供 d 標誌,將返回每個捕獲組的索引。如果您將每個匹配的組與原始文字關聯起來,例如提供編譯器診斷,這尤其有用。

js
const code = `function add(x, y) {
  return x + y;
}`;
const functionRegexp =
  /(function\s+)(?<name>[$_\p{ID_Start}][$\p{ID_Continue}]*)/du;
const match = functionRegexp.exec(code);
const lines = code.split("\n");
lines.splice(
  1,
  0,
  " ".repeat(match.indices[1][1] - match.indices[1][0]) +
    "^".repeat(match.indices.groups.name[1] - match.indices.groups.name[0]),
);
console.log(lines.join("\n"));
// function add(x, y) {
//          ^^^
//   return x + y;
// }

另見