ECMA Script 6_RegExp 正則表達式

在 ES5 中git

RegExp 構造函數的參數有兩種狀況github

  • RegExp(字符串, 正則表達式的修飾符)
  • RegExp(正則表達式);
  • var regex = new RegExp('xyz', 'i');
    
    // 等價於
    var regex = new RegExp(/xyz/i);    // 不能再有 第二參數修飾符,不然會報錯 // 等價於
    var regex = /xyz/i;
  • lastIndex 屬性 指定每次搜索的開始位置,修飾符從這個位置開始向後搜索,直到發現匹配爲止
  • const REGEX = /a/g;
    
    REGEX.lastIndex = 2;    // 指定從2號位置(y)開始匹配
    const match = REGEX.exec('xaya');    // 匹配成功
    match.index;     // 3    // 在3號位置匹配成功
    
    
    REGEX.lastIndex;    // 4    // 下一次匹配從4號位開始
    REGEX.exec('xaya');    // null    // 4號位開始匹配失敗

在 ES6 中正則表達式

若是 RegExp 構造函數第一個參數是一個正則對象,那麼仍是可使用第二個參數指定修飾符。數組

並且,返回的正則表達式會忽略原有的正則表達式的修飾符,只使用新指定的修飾符ide

  • new RegExp(/abc/ig, 'i').flags // "i" 而不是 'ig'
  • 字符串的正則方法

符串對象共有 4 個方法,可使用正則表達式:match()replace()search()split()函數

ES6 將這 4 個方法,在語言內部所有調用 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]
  • 添加了 修飾符,含義爲「Unicode 模式」,

用來正確處理大於 \uFFFF 的 Unicode 字符。spa

也就是說,會正確處理四個字節的 UTF-16 編碼prototype

  • /^\uD83D/u.test('\uD83D\uDC2A');    // false
    /^\uD83D/.test('\uD83D\uDC2A');    // true
  • 必須加 u 修飾符 的狀況
  • ES6 新增了使用大括號表示 Unicode 字符,這種表示法在正則表達式中必須加上u修飾符,才能識別當中的大括號,不然會被解讀爲量詞
  • /\u{61}/.test('a');    // false
    /\u{61}/u.test('a');    // true
    /\u{20BB7}/u.test('𠮷');    // true
  • \S 是預約義模式,匹配全部非空白字符。只有加了u修飾符,它才能正確匹配碼點大於0xFFFF的 Unicode 字符
  • /^\S$/.test('𠮷');    // false
    /^\S$/u.test('𠮷');    // true

利用這個特性,封裝一個絕對正確地返回字符串長度的函數設計

  • function strLength(str) {
        var result = str.match(/[\s\S]/gu);
        return result ? result.length : 0;
    };
    
    var s = '𠮷𠮷';
    
    s.length // 4
    codePointLength(s) // 2
  • 正則實例對象新增 unicode 屬性

表示是否設置了u修飾符

RegExp.prototype.unicode

  • const r1 = /hello/;
    const r2 = /hello/u;
    
    r1.unicode;    // false
    r2.unicode;    // true
  • y 修飾符——「粘連」(sticky)修飾符

做用與 g 修飾符相似,也是全局匹配,後一次匹配都從上一次匹配成功的下一個位置開始。

不一樣之處在於,g 修飾符 只要 剩餘位置中存在匹配 就可,

而 y 修飾符 確保匹配必須從剩餘的第一個位置開始,這也就是「粘連」的涵義

  • y 修飾符一樣遵照 lastIndex 屬性,可是要求必須在 lastIndex 指定的位置發現匹配
  • const REGEX = /a/y;
    
    REGEX.lastIndex = 2;    // 指定從2號位置開始匹配
    REGEX.exec('xaya');    // null    不是粘連,匹配失敗
    REGEX.lastIndex = 3;    // 指定從3號位置開始匹配
    
    const match = REGEX.exec('xaya');    // 3號位置是粘連,
    match.index;    // 3    匹配成功
    REGEX.lastIndex;    // 4
  • 修飾符的設計本意,就是讓頭部匹配的標誌^在全局匹配中都有效
  • /b/y.exec('aba');    // null    因爲不能保證頭部匹配 b,因此返回null
    
    const REGEX = /a/gy;
    'aaxa'.replace(REGEX, '-');    // '--xa'   最後一個由於不是出如今下一次匹配的頭部,因此不會被替換a
  • 單單一個y修飾符對 match 方法,只能返回第一個匹配,必須與 修飾符聯用,才能返回全部匹配
  • 'a1a2a3'.match(/a\d/y);    // ["a1"]
    'a1a2a3'.match(/a\d/gy);    // ["a1", "a2", "a3"]
  • 應用

從字符串提取 token(詞元),修飾符確保了匹配之間不會有漏掉的字符

  • function tokenize(TOKEN_REGEX, str) {
        let result = [];
        let match;
        while (match = TOKEN_REGEX.exec(str)) {
            result.push(match[1]);
        };
        return result;
    };
    
    
    const TOKEN_Y = /\s*(\+|[0-9]+)\s*/y;
    const TOKEN_G  = /\s*(\+|[0-9]+)\s*/g;
    
    tokenize(TOKEN_Y, '3 + 4');    // [ '3', '+', '4' ]
    tokenize(TOKEN_G, '3 + 4');    // [ '3', '+', '4' ]
    
    // g修飾符會忽略非法字符,而 y 修飾符不會
    tokenize(TOKEN_Y, '3x + 4');    // [ '3', '+', '4' ]
    tokenize(TOKEN_G, '3x + 4');    // ['3']    不是咱們想要的現象

RegExp.prototype.sticky

是否設置了 修飾符

  • var r = /hello\d/y;
    r.sticky;    // true    var r = /hello\d/y;
    r.sticky;    // true
  • ES6 爲正則表達式新增了 flags 屬性,會返回正則表達式的修飾符
  • // ES5 的 source 屬性
    // 返回正則表達式的正文
    /abc/ig.source    // "abc"
    
    // ES6 的 flags 屬性
    // 返回正則表達式的修飾符
    /abc/ig.flags    // 'gi'
  • s 修飾

dotAll 模式,即 點(dot)表明一切字符。

s修飾符,使得 . 能夠匹配任意單個字符

RegExp.prototype.dotAll

返回一個布爾值,表示該正則表達式是否處在dotAll模式

  • const re = /foo.bar/s;
    
    // 另外一種寫法
    // const re = new RegExp('foo.bar', 's');
    
    re.test('foo\nbar');     // true    正確地將 \n 識別了出來
     re.dotAll; // true
    re.flags;     // 's'

JavaScript 語言的正則表達式 斷言

以前只支持

  • 先行斷言(lookahead)

只有在 前面才匹配,必須寫成/x(?=y)/

好比,只匹配百分號以前的數字,要寫成/\d+(?=%)/

  • 先行否認斷言(negative lookahead)

x 只有不在 y 前面才匹配,必須寫成/x(?!y)/

好比,只匹配不在百分號以前的數字,要寫成/\d+(?!%)/

  • /\d+(?=%)/.exec('100% of US presidents have been male');    // ["100"]
    /\d+(?!%)/.exec('that’s all 44 of them');    // ["44"]

ES6 引入

  • 後行斷言(lookbehind)

只有在 後面才匹配,必須寫成/(?<=y)x/

如,只匹配美圓符號以後的數字,要寫成/(?<=\$)\d+/

  • 後行否認斷言(negative lookbehind)

只有不在 後面才匹配,必須寫成/(?<!y)x/

好比,只匹配不在美圓符號後面的數字,要寫成/(?<!\$)\d+/

  • /(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill');    // ["100"]
    /(?<!\$)\d+/.exec('it’s is worth about €90');    // ["90"]

Unicode 屬性類

 

\p{UnicodePropertyName=UnicodePropertyValue}

一種新的類的寫法 \p{...} 和 \P{...},容許正則表達式匹配符合 Unicode 某種屬性的全部字符

這兩種類只對 Unicode 有效,因此使用的時候必定要加上u修飾符

若是不加u修飾符,正則表達式使用 \p 和 \P 會報錯,ECMAScript 預留了這兩個類

  • 對於某些屬性,能夠只寫屬性名,或者只寫屬性值
  • \p{UnicodePropertyName}
    \p{UnicodePropertyValue}

\P{…}\p{…} 的反向匹配,即匹配不知足條件的字符

  • 屬性類指定匹配全部十進制字符,能夠看到各類字型的十進制字符都會匹配成功
  • const regex = /^\p{Decimal_Number}+$/u;
    regex.test('𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼');    // true
  • \p{Number} 甚至能匹配羅馬數字
  • // 匹配全部數字
    const regex = /^\p{Number}+$/u;
    regex.test('²³¹¼½¾');    // true
    regex.test('㉛㉜㉝');    // true
    regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ');    // true
  • // 匹配全部空格 \p{White_Space} // 匹配各類文字的全部字母,等同於 Unicode 版的 \w [\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}] // 匹配各類文字的全部非字母的字符,等同於 Unicode 版的 \W [^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}] // 匹配 Emoji /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu // 匹配全部的箭頭字符 const regexArrows = /^\p{Block=Arrows}+$/u; regexArrows.test('←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩') // true

具名組匹配(Named Capture Groups)

容許爲每個組匹配指定一個名字/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/

既便於閱讀代碼,又便於引用

  • const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
    
    const matchObj = RE_DATE.exec('1999-12-31');
    const year = matchObj.groups.year;    // 1999
    const month = matchObj.groups.month;    // 12
    const day = matchObj.groups.day;    // 31

同時,數字序號(matchObj[1])依然有效

若是具名組沒有匹配,那麼對應的 groups 對象屬性會是 undefined

具名組 as 沒有找到匹配,那麼 matchObj.groups.as 屬性值就是 undefined,而且 as 這個鍵名在 groups 是始終存在的

  • 若是要在正則表達式內部引用某個「具名組匹配」,可使用 \k<組名> 的寫法
  • const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
    RE_TWICE.test('abc!abc');    // true
    RE_TWICE.test('abc!ab');    // false
  • 數字引用(\1)依然有效
  • const RE_TWICE = /^(?<word>[a-z]+)!\1$/;
    RE_TWICE.test('abc!abc');    // true
    RE_TWICE.test('abc!ab');    // false
  • 兩種引用語法還能夠同時使用
  • const RE_TWICE = /^(?<word>[a-z]+)!\k<word>!\1$/;
    RE_TWICE.test('abc!abc!abc');    // true
    RE_TWICE.test('abc!abc!ab');    // false

String.prototype.matchAll()

能夠一次性取出全部匹配。

不過,它返回的是一個遍歷器(Iterator),而不是數組

相對於返回數組,返回遍歷器的好處在於,若是匹配結果是一個很大的數組,那麼遍歷器比較節省資源

  • 遍歷器轉爲數組是很是簡單的,使用 ...運算符 和 Array.from() 方法就能夠了
  • // 轉爲數組方法一
    [...string.matchAll(regex)]
    
    // 轉爲數組方法二
    Array.from(string.matchAll(regex));
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息