ES6學習 第五章 正則的擴展

前言

本章介紹正則的擴展。有些不常用的知識了解即可。
本章原文鏈接:正則的擴展

RegExp 構造函數

從 ES6 開始,如果RegExp構造函數第一個參數是一個正則對象,並且第二個標誌存在且為標誌參數,將不再拋出 TypeError ,將使用這些參數創建一個新的正則表達式。原有的正則表達式修飾符將被忽略

const flag = new RegExp(/[0-9]/ig, 'i').flags; // 原有修飾符衛 ig ,被 i 給替代了
console.log(flag); // i

字元串有關正則表達式

ES6將之前字元串上的四個關於正則表達式的方法全部更改為RegExp的實例方法,所以現在所有與正則表達式有關的方法,全部定義在RegExp對象上。

  • String.prototype.match 調用 RegExp.prototype[Symbol.match]
  • String.prototype.replace 調用 RegExp.prototype[Symbol.replace]
  • String.prototype.search調用 RegExp.prototype[Symbol.search]
  • String.prototype.split調用 RegExp.prototype[Symbol.split]

flags 屬性

RegExp.prototype.flags 屬性 是ES6新增屬性,會返回正則表達式的修飾符。

const SAMPLEREG = /abc/ig;
console.log(SAMPLEREG.flags); // gi

u 修飾符

在ES6中新增了 u修飾符,表示使用Unicode碼的模式進行匹配。處理大於\uFFFFUnicode 字元

注意

一旦加上u修飾符號,就會修改下面這些正則表達式的行為。

  1. 點字元

對於碼點大於0xFFFFUnicode 字元,點字元不能識別,必須加上u修飾符。

  1. **Unicode**** 字元表示法**

新增了使用大括弧表示 Unicode 字元,這種表示法在正則表達式中必須加上u修飾符,才能識別當中的大括弧,否則會被解讀為量詞。

  1. 量詞

使用u修飾符後,所有量詞都會正確識別碼點大於0xFFFFUnicode 字元。

  1. 預定義模式

u修飾符也影響到預定義模式,能否正確識別碼點大於0xFFFF的 Unicode 字元。

  1. i 修飾符

有些 Unicode 字元的編碼不同,但是字型很相近,比如,\u004B\u212A都是大寫的K

  1. 轉義

沒有u修飾符的情況下,正則中沒有定義的轉義(如逗號的轉義\,)無效,而在u模式會報錯。

unicode 屬性

RegExp.prototype.unicode 屬性表明正則表達式帶有”u” 標誌。 unicode 是正則表達式獨立實例的只讀屬性。

const SAMPLEREG = /abc/u;

console.log(SAMPLEREG.flags); // u
console.log(SAMPLEREG.unicode); // true

Unicode 屬性類

**Unicode property escapes**
ES2018 引入了一種新的類的寫法\p{...}和`P{…},用於解決 JavaScript 沒有強有效的方式用匹配出不同文字問題。允許正則表達式匹配符合 Unicode` 某種屬性的所有字元。

\p{Unicode屬性名=Unicode屬性值}
// 對於某些屬性,可以只寫屬性名,或者只寫屬性值。
\p{Unicode屬性值}
\p{Unicode屬性名}

// \P 為 \p 取反
\P{Unicode屬性值}
\P{Unicode屬性名}

注意:
這兩種類只對 Unicode 有效,所以使用的時候一定要加上u修飾符。
\P{…}\p{…}的反向匹配,即匹配不滿足條件的字元。

const SAMPLEREG = /\p{Script=Greek}/u;
SAMPLEREG.test('π'); // true

y 修飾符

y 修飾符的作用

在ES6中新增了 y 修飾符,表示執行「粘性(sticky)」搜索,匹配從目標字元串的當前位置開始。

y修飾符與g修飾符相似,都是全局匹配,後一次匹配從上一次匹配成功的下一個位置開始。
區別是:g修飾符只要剩餘位置中存在匹配即可;而y修飾符必須從剩餘的第一個位置開始匹配。

// y修飾符與g修飾符的區別
const SAMPLE = 'abcdabcd';
const SAMPLEREG1 = /abcd/g;
const SAMPLEREG2 = /abcda/y;

console.log(SAMPLEREG1.test(SAMPLE)); // true
console.log(SAMPLEREG2.test(SAMPLE)); // true
console.log(SAMPLEREG1.test(SAMPLE)); // true
console.log(SAMPLEREG2.test(SAMPLE)); // false

注意

實際上,y修飾符號隱含了頭部匹配的標誌^

const SAMPLEREGGY = /ab/gy;
const SAMPLEREGY = /ab/y;

let sample1 = 'ababcabcd'.replace(SAMPLEREGGY, '-'); 
let sample2 = 'ababcabcd'.replace(SAMPLEREGY, '-');

// 最後一個ab因為不是出現在下一次匹配的頭部,所以不會被替換。
console.log(sample1);
// 只能返回第一個匹配,必須與g修飾符聯用,才能返回所有匹配。
console.log(sample2);

sticky 屬性

RegExp.prototype.sticky表示是否設置了y修飾符。sticky 是正則表達式對象的只讀屬性。

const SAMPLEREG = /a/gy;
console.log(SAMPLEREG.sticky); // true

s 修飾符

ES2018 引入s修飾符,使得.可以匹配任意單個字元。包括行終止符(line terminator character)。

行終止符

所謂行終止符,就是該字元表示一行的終結。以下四個字元屬於「行終止符」。

  • U+000A 換行符(\n
  • U+000D 回車符(\r
  • U+2028 行分隔符(line separator
  • U+2029 段分隔符(paragraph separator
const SAMPLEREG = /ab.cd/s;
console.log(SAMPLEREG.test('ab\ncd') ); // true

dotAll

上面這種情況被稱為**dotAll**模式,即點(dot)代表一切字元。正則表達式還引入了一個**dotAll**屬性
dotAll 屬性返回一個布爾值,表明是否在正則表達式中一起使用”s“修飾符。dotAll 是一個只讀的屬性,屬於單個正則表達式實例。

    const SAMPLEREG = /ab.cd/s;
    const sample = SAMPLEREG.test('ab\ncd');
    console.log(SAMPLEREG.flags); // s
    console.log(SAMPLEREG.dotAll); // true

後行斷言

ES2018 引入後行斷言,V8 引擎 4.9 版(Chrome 62)已經支援。

  • 先行斷言
    x只有在y前面才匹配,必須寫成/x(?=y)/
    比如,只匹配百分號之前的數字,要寫成/\d+(?=%)/

  • 先行否定斷言
    x只有不在y前面才匹配,必須寫成/x(?!y)/
    比如,只匹配不在百分號之前的數字,要寫成/\d+(?!%)/

  • 後行斷言正好與先行斷言相反,
    x只有在y後面才匹配,必須寫成/(?<=y)x/
    比如,只匹配美元符號之後的數字,要寫成/(?<=\$)\d+/

  • 後行否定斷言則與先行否定斷言相反,
    x只有不在y後面才匹配,必須寫成/(?<!y)x/
    比如,只匹配不在美元符號後面的數字,要寫成/(?<!\$)\d+/

後行斷言需要先匹配/(?<=y)x/x,然後再回到左邊,匹配y的部分。順序為先右後左

// 先行斷言
const sample1 = /\d+(?=%)/.exec('100% of US presidents have been male');
// 先行否定斷言
const sample2 = /\d+(?!%)/.exec('that』s all 44 of them');
console.log(sample1);  // 100
console.log(sample2);  // 44

// 後行斷言
const sample3 = /(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill');
// 後行否定斷言
const sample4 = /(?<!\$)\d+/.exec('it』s is worth about €90');
console.log(sample3);  // 100
console.log(sample4);  // 90

組匹配

正則表達式的括弧表示分組匹配,括弧中的模式可以用來匹配分組的內容。

ES2018 引入了具名組匹配(Named Capture Groups),允許為每一個組匹配指定一個名字,既便於閱讀程式碼,又便於引用。
具名組匹配在圓括弧內部,模式的頭部添加「問號 + 尖括弧 + 組名」(?<year>),然後就可以在exec方法返回結果的groups屬性上引用該組名。同時,數字序號依然有效。

const sampleUsers = `
姓劉名備字玄德
姓關名羽字雲長
姓張名飛字翼德`;
const SAMPLEREG = /姓(?<surnames>.+)名(?<name>.+)字(?<word>.+)/g;
let result = SAMPLEREG.exec(sampleUsers);

do { console.log(`${result.groups.surnames}${result.groups.name}${result.groups.surnames}${result.groups.word}`);
} while ((result = SAMPLEREG.exec(sampleUsers)) !== null);

/*
* 劉備劉玄德
* 關羽關雲長
* 張飛張翼德
*/

上面的程式碼中:?<xxx>的作用就是為這個匹配定義一個組名,在匹配的groups屬性中可以查看到匹配的組名,這裡可以使用解構賦值直接從匹配結果上為變數賦值。

注意 : 如果要在正則表達式內部引用某個具名組匹配,可以使用\k<組名>的寫法

matchAll()

ES2020 增加了String.prototype.matchAll()方法,可以一次性取出所有匹配。不過,它返回的是一個遍歷器/迭代器(Iterator),而不是數組。

const string = 'sample1sample2sample3';
const regex = /sample/g;

for (const match of string.matchAll(regex)) {
  console.log(match);
}
// 遍歷輸出
/*
['sample', index: 0, input: 'sample1sample2sample3', groups: undefined]
['sample', index: 7, input: 'sample1sample2sample3', groups: undefined]
['sample', index: 14, input: 'sample1sample2sample3', groups: undefined]
*/