<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 39. 正則表達式(`RegExp`) > 原文: [http://exploringjs.com/impatient-js/ch_regular-expressions.html](http://exploringjs.com/impatient-js/ch_regular-expressions.html) > > 貢獻者:[iChrisJ](https://github.com/iChrisJ) > ![](https://img.kancloud.cn/83/6f/836f0345de4fda5b9b81d7038ebb276d.svg)**功能的可用性** > 除非另有說明,否則 ES5 及更高版本支持所有正則表達式功能。 ### 39.1. 創建正則表達式 #### 39.1.1. 字面義式 vs. 構造函數式 創建正則表達式的兩種主要方法是: * 字面義式:`/abc/ui`,被靜態編譯(在加載時)。 * 構造函數式:`new RegExp('abc', 'ui')`,被動態編譯(在運行時) * 第二個參數是可選的。 兩個正則表達式都有相同的兩個部分: * _表達式體_ `abc` - 實際正則表達式。 * _標志_ `u` 和 `i`。標志配置了表達式模式如何被解釋:`u`切換到 [_Unicode 模式 _](ch_regular-expressions.html#regexp-unicode-mode) 。 `i`啟用大小寫不敏感匹配。 #### 39.1.2. 克隆和非破壞性地修改正則表達式 構造函數`RegExp()`有兩種變體: * `new RegExp(pattern : string, flags = '')` 通過指定的`pattern`創建新的正則表達式。如果缺少`flags`,則使用空字符串`''`。 * `new RegExp(regExp : RegExp, flags = regExp.flags)`<sup>[ES6]</sup> `regExp`被克隆。如果提供了`flags`,則它確定副本的標志。 第二個變體可用于克隆正則表達式,在修改它們時也是可選的。標志是不可變的,這是改變它們的唯一方法。例如: ```JavaScript function copyAndAddFlags(regExp, flags='') { // The constructor doesn’t allow duplicate flags, // make sure there aren’t any: const newFlags = [...new Set(regExp.flags + flags)].join(''); return new RegExp(regExp, newFlags); } assert.equal(/abc/i.flags, 'i'); assert.equal(copyAndAddFlags(/abc/i, 'g').flags, 'gi'); ``` ### 39.2. 語法 #### 39.2.1. 語法字符 在正則表達式的頂層,以下 _語法字符_ 是特殊的。它們通過前綴反斜杠(`\`)進行轉義。 ```JavaScript \ ^ $ . * + ? ( ) [ ] { } | ``` 在正則表達式字面義中,您還必須轉義斜杠(用`new RegExp()`就沒有必要了): ```JavaScript > /\//.test('/') true > new RegExp('/').test('/') true ``` #### 39.2.2. 基本原子 _原子_ 是正則表達式的基本構建塊。 * 模式字符:除語法字符(`^`,`$`等)外的所有字符。模式字符匹配自己。示例:`A b %` * `.`匹配任何字符。您可以使用標志`/s`(`dotall`)來控制點`.`是否與行終止符匹配([下面詳述](ch_regular-expressions.html#reg-exp-flags))。 * 字符轉義(每個轉義匹配一個固定字符): * 控制轉義(用于一些控制字符): * `\f`:換頁(FF) * `\n`:換行(LF) * `\r`:回車(CR) * `\t`:角色列表 * `\v`:行列表 * 任意控制字??符:`\cA`(Ctrl-A),`\cB`(Ctrl-B)等。 * Unicode 代碼單位:`\u00E4` * Unicode 代碼點(需要標志`/u`):`\u{1F44D}` * 字符類轉義(每個轉義匹配一組字符中的一個): * `\d`:數字(與`[0-9]`相同) * `\D`:非數字 * `\w`:“單詞”字符(與`[A-Za-z0-9_]`相同) * `\W`:非單詞字符 * `\s`:空白(空格,制表符,行終止符等) * `\S`:非空白 * Unicode 屬性轉義(ES2018):`\p{White_Space}`,`\P{White_Space}`等. * 需要標志`/u`。 * 在下一小節中描述。 ##### 39.2.2.1. Unicode 屬性轉義 Unicode 屬性轉義看起來像這樣: 1. `\p{prop=value}`:匹配屬性`prop`具有值`value`的所有字符。 2. `\P{prop=value}`:匹配所有沒有屬性`prop`的字符,其值為`value`。 3. `\p{bin_prop}`:匹配二進制屬性`bin_prop`為 True 的所有字符。 4. `\P{bin_prop}`:匹配二進制屬性`bin_prop`為 False 的所有字符。 評論: * 如果設置了標志`/u`,則只能使用 Unicode 屬性轉義。沒有`/u`,`\p`與`p`相同。 * 如果屬性是`General_Category`,則表格(3)和(4)可以用作縮寫。例如,`\p{Lowercase_Letter}`是`\p{General_Category=Lowercase_Letter}`的縮寫 例子: * 檢查空格: ```JavaScript > /^\p{White_Space}+$/u.test('\t \n\r') true ``` * 檢查希臘字母: ```JavaScript > /^\p{Script=Greek}+$/u.test('μετ?') true ``` * 刪除任何字母: ```JavaScript > '1π2ü3é4'.replace(/\p{Letter}/ug, '') '1234' ``` * 刪除小寫字母: ```JavaScript > 'AbCdEf'.replace(/\p{Lowercase_Letter}/ug, '') 'ACE' ``` 進一步閱讀: * Unicode 屬性及其值的列表:[“Unicode 標準附件#44:Unicode 字符數據庫”](https://unicode.org/reports/tr44/#Properties)(編輯:Mark Davis,Lauren?iuIancu,Ken Whistler) * Unicode 屬性更深入地轉義:“探索 ES2018 和 ES2019”中的章節[“RegEx Unicode 屬性轉義”](http://exploringjs.com/es2018-es2019/ch_regexp-unicode-property-escapes.html) #### 39.2.3。角色類 * 匹配一組字符中的一個:`[abc]` * 匹配不在集合中的任何字符:`[^abc]` * 在方括號內,只有以下字符是特殊的,必須進行轉義: ```JavaScript ^ \ - ] ``` `^`只有先到時才需要進行轉義。 `-`如果是第一個或最后一個,則無需轉義 * 字符轉義(`\n`,`\u{1F44D}`)和字符類轉義(`\d`,`\p{White_Space}`)照常工作。 * 例外:在方括號內,`\b`匹配退格。在其他地方,它匹配單詞邊界。 * 字符范圍通過短劃線指定:`[a-z]`,`[^a-z]` #### 39.2.4. 組 * 位置捕獲組:`(#+)` * 反向引用:`\1`,`\2`等 * 命名捕獲組(ES2018):`(?<hashes>#+)` * 反向引用:`\k<hashes>` * 非捕獲組:`(?:#+)` #### 39.2.5. 量詞 默認情況下,以下所有量詞都是貪心的: * `?`:匹配從不或一次 * `*`:匹配零次或多次 * `+`:匹配一次或多次 * `{n}`:匹配`n`次 * `{n,}`:匹配`n`次或更多次 * `{n,m}`:至少匹配`n`次,最多`m`次。 為了使他們不情愿,在他們后面加上問號(`?`): ```JavaScript > /".*"/.exec('"abc"def"')[0] // greedy '"abc"def"' > /".*?"/.exec('"abc"def"')[0] // reluctant '"abc"' ``` #### 39.2.6. 斷言 * `^`僅在輸入的開頭匹配 * `$`僅在輸入結束時匹配 * `\b`僅匹配單詞邊界 * `\B`僅在不在單詞邊界時匹配 * 向前看: * 如果`pattern`匹配下一個(積極前瞻),則`(?=?pattern?)`匹配。示例(“`X`后跟小寫字母的序列” - 請注意`X`本身不是匹配的子字符串的一部分): ```JavaScript > 'abcX def'.match(/[a-z]+(?=X)/g) [ 'abc' ] ``` * 如果`pattern`與接下來的內容不匹配,則`(?!?pattern?)`匹配(消極前瞻)。示例(“小寫字母的序列,后面沒有`X`”) ```JavaScript > 'abcX def'.match(/[a-z]+(?!X)/g) [ 'ab', 'def' ] ``` * 進一步閱讀:[“探索 ES2018 和 ES2019”中的“RegExp lookbehind 斷言”](http://exploringjs.com/es2018-es2019/ch_regexp-lookbehind-assertions.html)(也涵蓋了先行斷言) * 向后看 (ES2018): * 如果`pattern`與之前的相符,則`(?<=?pattern?)`匹配(積極后觀) ```JavaScript > 'Xabc def'.match(/(?<=X)[a-z]+/g) [ 'abc' ] ``` * 如果`pattern`與之前的不匹配,則`(?<!?pattern?)`匹配(消極后觀) ```JavaScript > 'Xabc def'.match(/(?<!X)[a-z]+/g) [ 'bc', 'def' ] ``` * 進一步閱讀:[“探索 ES2018 和 ES2019”中的“RegExp lookbehind 斷言”](http://exploringjs.com/es2018-es2019/ch_regexp-lookbehind-assertions.html) #### 39.2.7. 分離(`|`) 警告:此運算符的優先級較低。必要時使用組: * `^aa|zz$`匹配以`aa`開頭和/或以`zz`結束的所有字符串。請注意,`|`的優先級低于`^`和`$`。 * `^(aa|zz)$`匹配兩個字符串`'aa'`和`'zz'`。 * `^a(a|z)z$`匹配兩個字符串`'aaz'`和`'azz'`。 ### 39.3. 標志 表格 20: 這些是JavaScript支持的正則表達式標志。 | 字面義標志 | 屬性名稱 | ES | 描述 | | --- | --- | --- | --- | | `g` | `global` | ES3 | 匹配多次 | | `i` | `ignoreCase` | ES3 | 不區分大小寫 | | `m` | `multiline` | ES3 | 每行`^`和`$`匹配 | | `s` | `dotall` | ES2018 | 點`.`匹配行終止符 | | `u` | `unicode` | ES6 | Unicode 模式(推薦) | | `y` | `sticky` | ES6 | 匹配之間沒有字符 | JavaScript 中提供了以下正則表達式標志(表格[20](#tbl:reg-exp-flags-table) 提供了緊湊的概述): * `/g`(`.global`):從根本上改變方法`RegExp.prototype.test()`,`RegExp.prototype.exec()`和`String.prototype.match()`的工作方式。將與這些方法一起詳細解釋。簡而言之:如果沒有`/g`,方法只考慮輸入字符串中正則表達式的第一個匹配項。使用`/g`,他們會考慮所有匹配。 * `/i`(`.ignoreCase`):打開不區分大小寫的匹配: ```JavaScript > /a/.test('A') false > /a/i.test('A') true ``` * `/m`(`.multiline`):如果該標志打開,`^`匹配每一行的開頭,`$`匹配每一行的結尾。如果它關閉,`^`匹配整個輸入字符串的開頭,`$`匹配整個輸入字符串的結尾。 ```JavaScript > 'a1\na2\na3'.match(/^a./gm) [ 'a1', 'a2', 'a3' ] > 'a1\na2\na3'.match(/^a./g) [ 'a1' ] ``` * `/u`(`.unicode`):該標志用于打開正則表達式的 Unicode 模式。該模式將在下一小節中解釋。 * `/y`(`.sticky`):該標志僅與`/g`一起使用。當兩者都打開時,第一個之后的任何匹配必須直接跟隨前一個匹配(它們之間沒有任何字符)。 ```JavaScript > 'a1a2 a3'.match(/a./gy) [ 'a1', 'a2' ] > 'a1a2 a3'.match(/a./g) [ 'a1', 'a2', 'a3' ] ``` * `/s`(`.dotall`):默認情況下,點與行終止符不匹配。有了這個標志,它確實: ```JavaScript > /./.test('\n') false > /./s.test('\n') true ``` 舊版 ECMAScript 版本的替代方案: ```JavaScript > /[^]/.test('\n') true ``` #### 39.3.1. 標志:通過`/u`的 Unicode 模式 標志`/u`為正則表達式打開特殊的 Unicode 模式。該模式支持多種功能: * 在模式中,您可以使用 Unicode 代碼點轉義(例如`\u{1F42A}`)來指定字符。諸如`\u03B1`之類的代碼單元轉義只有四個十六進制數字的范圍(等于基本的多語言平面)。 * 在模式中,您可以使用 Unicode 屬性轉義(ES2018),例如`\p{White_Space}`。 * 現在禁止許多轉義(這使得之前的功能成為可能): ```JavaScript > /\a/ /\a/ > /\a/u SyntaxError: Invalid regular expression: /\a/: Invalid escape > /\-/ /\-/ > /\-/u SyntaxError: Invalid regular expression: /\-/: Invalid escape > /\:/ /\:/ > /\:/u SyntaxError: Invalid regular expression: /\:/: Invalid escape ``` * 匹配的原子單位(“字符”)是代碼點,而不是代碼單元。 以下小節將更詳細地解釋最后一項。它們使用以下 Unicode 字符來解釋原子單位何時是代碼點以及何時是代碼單元: ```JavaScript const codePoint = '??'; const codeUnits = '\uD83D\uDE42'; // UTF-16 assert.equal(codePoint, codeUnits); // same string! ``` 我只在`??`和`\uD83D\uDE42`之間切換,以說明 JavaScript 如何看待事物。兩者都是等價的,可以在字符串和正則表達式中互換使用。 ##### 39.3.1.1. 結果:您可以將代碼點放在字符類中 使用`/u`,`??`的兩個代碼單元被解釋為單個字符: ```JavaScript > /^[??]$/u.test('??') true ``` 沒有`/u`,`??`被解釋為兩個字符: ```JavaScript > /^[\uD83D\uDE42]$/.test('\uD83D\uDE42') false > /^[\uD83D\uDE42]$/.test('\uDE42') true ``` 請注意,`^`和`$`要求輸入字符串具有單個字符。這就是為什么第一個結果是`false`。 ##### 39.3.1.2. 結果:點運算符(`.`)匹配代碼點,而不是代碼單元 使用`/u`,點運算符匹配代碼點(`.match()`加`/g`返回一個包含正則表達式的所有匹配項的數組): ```JavaScript > '??'.match(/./gu).length 1 ``` 沒有`/u`,點運算符匹配單個代碼單元: ```JavaScript > '\uD83D\uDE80'.match(/./g).length 2 ``` ##### 39.3.1.3. 后果:量詞適用于代碼點,而不是代碼單元 使用`/u`,量詞適用于整個前面的代碼點: ```JavaScript > /^??{3}$/u.test('??????') true ``` 沒有`/u`,量詞僅適用于前面的代碼單元: ```JavaScript > /^\uD83D\uDE80{3}$/.test('\uD83D\uDE80\uDE80\uDE80') true ``` ### 39.4. 正則表達式對象的屬性 值得注意的是: * 嚴格地說,只有`.lastIndex`是一個真實的實例屬性。所有其他屬性都通過 getter 實現。 * 因此,`.lastIndex`是唯一可變的屬性。所有其他屬性都是只讀的。如果要更改它們,則需要復制正則表達式(有關詳細信息,請參閱[克隆部分](ch_regular-expressions.html#cloning-regexps))。 #### 39.4.1. 標志作為屬性 每個正則表達式標志都作為屬性存在,具有更長,更具描述性的名稱: ```JavaScript > /a/i.ignoreCase true > /a/.ignoreCase false ``` 這是標志屬性的完整列表: * `.dotall`(`/s`) * `.global`(`/g`) * `.ignoreCase`(`/i`) * `.multiline`(`/m`) * `.sticky`(`/y`) * `.unicode`(`/u`) #### 39.4.2. 其他財產 每個正則表達式還具有以下屬性: * `.source`:正則表達式模式。 ```JavaScript > /abc/ig.source 'abc' ``` * `.flags`:正則表達式的標志。 ```JavaScript > /abc/ig.flags 'gi' ``` * `.lastIndex`:當標志`/g`打開時使用。有關詳細信息,請參閱[關于該標志](ch_regular-expressions.html#regexp-flag-g)的部分。 ### 39.5. 使用正則表達式的方法 #### 39.5.1. `regExp.test(str)`:有匹配嗎? 如果`regExp`與`str`匹配,則正則表達式方法`.test()`返回`true`: ```JavaScript > /abc/.test('ABC') false > /abc/i.test('ABC') true > /\.js$/.test('main.js') true ``` 使用`.test()`時,通常應避免使用`/g`標志。如果您使用它,每次調用方法時通常不會得到相同的結果: ```JavaScript > const r = /a/g; > r.test('aab') true > r.test('aab') true > r.test('aab') false ``` 結果是由于`/a/`在字符串中有兩個匹配項。找到所有這些后,`.test()`返回`false`。 #### 39.5.2. `str.search(regExp)`:匹配的是什么指數? 字符串方法`.search()`返回`regExp`的第一個索引,其中`regExp`匹配: ```JavaScript > '_abc_'.search(/abc/) 1 > 'main.js'.search(/\.js$/) 4 ``` #### 39.5.3. `regExp.exec(str)`:捕獲組 ##### 39.5.3.1. 獲取第一個匹配項的匹配對象 如果沒有標志`/g`,`.exec()`將返回`str`中`regExp`的第一個匹配的所有捕獲: ```JavaScript assert.deepEqual( /(a+)b/.exec('ab aab'), { 0: 'ab', 1: 'a', index: 0, input: 'ab aab', groups: undefined, } ); ``` 結果是 _ 匹配對象 _ 具有以下屬性: * `[0]`:正則表達式匹配的完整子字符串 * `[1]`:位置捕獲組 1(等) * `.index`:匹配發生在哪里? * `.input`:匹配的字符串 * `.groups`:命名捕獲組 ##### 39.5.3.2. 命名組(ES2018) 前一個示例包含一個位置組。以下示例演示了命名組: ```JavaScript const regExp = /^(?<key>[A-Za-z]+): (?<value>.*)$/u; assert.deepEqual( regExp.exec('first: Jane'), { 0: 'first: Jane', 1: 'first', 2: 'Jane', index: 0, input: 'first: Jane', groups: { key: 'first', value: 'Jane' }, } ); ``` 如您所見,命名組`key`和`value`也作為位置組存在。 ##### 39.5.3.3. 遍歷多個匹配項 如果要檢索正則表達式的所有匹配(而不僅僅是第一個),則需要打開標志`/g`。然后你可以多次調用`.exec()`并每次獲得另一個匹配項。在最后一個匹配項之后,`.exec()`返回`null`。 ```JavaScript > const regExp = /(a+)b/g; > regExp.exec('ab aab') { 0: 'ab', 1: 'a', index: 0, input: 'ab aab', groups: undefined } > regExp.exec('ab aab') { 0: 'aab', 1: 'aa', index: 3, input: 'ab aab', groups: undefined } > regExp.exec('ab aab') null ``` 因此,您可以循環所有匹配,如下所示: ```JavaScript const regExp = /(a+)b/g; const str = 'ab aab'; let match; // Check for null via truthiness // Alternative: while ((match = regExp.exec(str)) !== null) while (match = regExp.exec(str)) { console.log(match[1]); } // Output: // 'a' // 'aa' ``` 與`/g`共享正則表達式有一些陷阱,[稍后會解釋](ch_regular-expressions.html#regexp-flag-g)。 ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:通過`.exec()`** 提取引用文本 `exercises/reg-exp/extract_quoted_test.js` #### 39.5.4. `str.match(regExp)`:返回所有匹配的子串 沒有`/g`,`.match()`就像`.exec()`一樣 - 它返回一個匹配對象。 使用`/g`,`.match()`返回與`regExp`匹配的`str`的所有子串: ```JavaScript > 'ab aab'.match(/(a+)b/g) // important: /g [ 'ab', 'aab' ] ``` 如果沒有匹配,`.match()`返回`null`: ```JavaScript > 'xyz'.match(/(a+)b/g) null ``` 您可以使用 Or 運算符來保護自己免受`null`的影響: ```JavaScript const numberOfMatches = (str.match(regExp) || []).length; ``` #### 39.5.5. `str.replace(searchValue, replacementValue)` `.replace()`有幾種不同的模式,具體取決于您為其參數提供的值: * `searchValue`是...... * 沒有`/g`的正則表達式:替換第一次出現。 * 帶`/g`的正則表達式:替換所有出現的事件。 * 字符串:替換第一次出現(字符串逐字解釋,而不是正則表達式)。唉,這意味著字符串作為搜索值的用途有限。在本章的后面,你會發現[是一個工具函數,用于將任意文本轉換為正則表達式](ch_regular-expressions.html#escapeForRegExp)。 * `replacementValue`是...... * 字符串:描述替換 * 功能:計算替換 下一小節假設正在使用帶有`/g`的正則表達式。 ##### 39.5.5.1. `replacementValue`是一個字符串 如果替換值是字符串,則美元符號具有特殊含義 - 它插入與正則表達式匹配的內容: | 文本 | 結果 | | --- | --- | | `$$` | 單個`$` | | `$&` | 完整匹配項 | | `$`` | 匹配項前的文字 | | `$'` | 匹配項后的文字 | | `$n` | 位置捕獲組`n`(`n > 0`) | | `$<name>` | 命名捕獲組`name` | 示例:在匹配的子字符串之前,之內和之后插入文本。 ```JavaScript > 'a1 a2'.replace(/a/g, "($`|$&|$')") '(|a|1 a2)1 (a1 |a|2)2' ``` 示例:插入位置捕獲組。 ```JavaScript > const regExp = /^([A-Za-z]+): (.*)$/ug; > 'first: Jane'.replace(regExp, 'KEY: $1, VALUE: $2') 'KEY: first, VALUE: Jane' ``` 示例:插入命名捕獲組。 ```JavaScript > const regExp = /^(?<key>[A-Za-z]+): (?<value>.*)$/ug; > 'first: Jane'.replace(regExp, 'KEY: $<key>, VALUE: $<value>') 'KEY: first, VALUE: Jane' ``` ##### 39.5.5.2. `replacementValue`是一個功能 如果替換值是函數,則可以計算每個替換值。在下面的示例中,我們將我們找到的每個非負整數乘以 2。 ```JavaScript assert.equal( '3 cats and 4 dogs'.replace(/[0-9]+/g, (all) => 2 * Number(all)), '6 cats and 8 dogs' ); ``` 替換函數獲取以下參數。請注意它們與匹配對象的相似程度。這些參數都是位置的,但我已經包含了通常如何命名它們: * `all`:完全匹配 * `g1`:位置捕獲組 1 * 等等。 * `index`:匹配發生在哪里? * `input`:匹配的字符串 * `groups`:命名捕獲組(對象) ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:通過`.replace()`和命名組**更改引號 `exercises/reg-exp/change_quotes_test.js` #### 39.5.6. 使用正則表達式的其他方法 `String.prototype.split()`的第一個參數是字符串或正則表達式。如果是后者,則將組捕獲的子串添加到方法的結果中: ```JavaScript > 'a : b : c'.split(/( *):( *)/) [ 'a', ' ', ' ', 'b', ' ', ' ', 'c' ] ``` 有關更多信息,請參閱[有關字符串](ch_strings.html#string-api-extracting)的章節。 ### 39.6. 標志`/g`及其陷阱 如果打開`/g`,以下兩個正則表達式方法會執行異常操作: * `RegExp.prototype.exec()` * `RegExp.prototype.test()` 然后可以重復調用它們并在字符串中傳遞所有匹配項。正則表達式的屬性`.lastIndex`用于跟蹤字符串中的當前位置。例如: ```JavaScript const r = /a/g; assert.equal(r.lastIndex, 0); assert.equal(r.test('aa'), true); // 1st match? assert.equal(r.lastIndex, 1); // after 1st match assert.equal(r.test('aa'), true); // 2nd match? assert.equal(r.lastIndex, 2); // after 2nd match assert.equal(r.test('aa'), false); // 3rd match? assert.equal(r.lastIndex, 0); // start over ``` 那么 flag `/g`怎么會有問題呢?我們將首先探討問題然后解決問題。 #### 39.6.1. 問題:您無法使用標志`/g`內聯正則表達式 無法內聯帶有`/g`的正則表達式:例如,在以下`while`循環中,每次檢查條件時都會創建正則表達式。因此,它的`.lastIndex`始終為零,循環永遠不會終止。 ```JavaScript let count = 0; // Infinite loop while (/a/g.test('babaa')) { count++; } ``` #### 39.6.2. 問題:刪除`/g`可能會破壞代碼 如果代碼需要帶有`/g`的正則表達式并且在`.exec()`或`.test()`的結果上有一個循環,那么沒有`/g`的正則表達式會導致無限循環: ```JavaScript const regExp = /a/; // Missing: flag /g let count = 0; // Infinite loop while (regExp.test('babaa')) { count++; } ``` 為什么?因為`.test()`總是返回第一個結果,`true`,而不是`false`。 #### 39.6.3. 問題:添加`/g`可能會破壞代碼 使用`.test()`時,還有另一個警告:如果要檢查正則表達式是否與字符串匹配,則正則表達式必須不具有`/g`。否則,每次調用`.test()`時,通常會得到不同的結果: ```JavaScript > const r = /^X/g; > r.test('Xa') true > r.test('Xa') false ``` 通常,如果您打算以這種方式使用`.test()`,則不會添加`/g`。但是,例如,如果您使用相同的正則表達式進行測試和替換,則會發生這種情況。或者,如果您通過參數獲得正則表達式。 #### 39.6.4. 問題:如果`.lastIndex`不為零,代碼可能會中斷 創建正則表達式時,`.lastIndex`初始化為零。如果代碼曾經收到`.lastIndex`不為零的正則表達式,它可能會中斷。例如: ```JavaScript const regExp = /a/g; regExp.lastIndex = 4; let count = 0; while (regExp.test('babaa')) { count++; } assert.equal(count, 1); // should be 3 ``` 如果正則表達式被共享且未正確處理,則`.lastIndex`不為零可能相對容易發生。 #### 39.6.5. 處理`/g`和`.lastIndex` 請考慮以下情形:您想要實現一個函數`countOccurrences(regExp, str)`,它計算`regExp`在`str`內??的匹配頻率。你如何防止錯誤的`regExp`破壞你的代碼?我們來看看三種方法。 首先,如果未設置`/g`或`.lastIndex`不為零,則可以拋出異常: ```JavaScript function countOccurrences(regExp, str) { if (!regExp.global) { throw new Error('Flag /g of regExp must be set'); } if (regExp.lastIndex !== 0) { throw new Error('regExp.lastIndex must be zero'); } let count = 0; while (regExp.test(str)) { count++; } return count; } ``` 其次,您可以克隆參數。這具有額外的好處,即`regExp`不會改變。 ```JavaScript function countOccurrences(regExp, str) { const cloneFlags = regExp.flags + (regExp.global ? '' : 'g'); const clone = new RegExp(regExp, cloneFlags); let count = 0; while (clone.test(str)) { count++; } return count; } ``` 第三,您可以使用`.match()`計算出現次數 - 這些次數不會改變或取決于`.lastIndex`。 ```JavaScript function countOccurrences(regExp, str) { if (!regExp.global) { throw new Error('Flag /g of regExp must be set'); } return (str.match(regExp) || []).length; } ``` ### 39.7. 使用正則表達式的技巧 #### 39.7.1. 轉義正則表達式的任意文本 以下函數會轉義任意文本,以便在將其放入正則表達式中時逐字匹配: ```JavaScript function escapeForRegExp(str) { return str.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&'); // (A) } assert.equal(escapeForRegExp('[yes?]'), String.raw`\[yes\?\]`); assert.equal(escapeForRegExp('_g_'), String.raw`_g_`); ``` 在 A 行中,我們轉義所有語法字符。請注意,`/u`禁止許多逃逸:其中包括`\:`和`\-`。 這是您可以使用`escapeForRegExp()`多次替換任意文本的方法: ```JavaScript > const re = new RegExp(escapeForRegExp(':-)'), 'ug'); > ':-) :-) :-)'.replace(re, '??') '?? ?? ??' ``` #### 39.7.2. 匹配一切或什么也沒有 有時,您可能需要一個匹配所有內容的正則表達式。例如,作為標記值。 * 匹配所有內容:`/(?:)/`(空組匹配所有內容;使其無法捕獲,避免不必要的工作) ```JavaScript > /(?:)/.test('') true > /(?:)/.test('abc') true ``` * 什么都不匹配:`/.^/`(一旦匹配進展超出第一個字符,`^`就不再匹配了) ```JavaScript > /.^/.test('') false > /.^/.test('abc') false ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看