JavaScript >> Javascript 文檔 >  >> JavaScript

非參與組:跨瀏覽器的混亂

圍繞處理正則表達式非參與捕獲組(我將稱之為 NPCG)的跨瀏覽器問題提出了幾個挑戰。標準一開始就很爛,三大瀏覽器(IE、Firefox、Safari)都以自己獨特的方式不尊重規則。

首先,我應該解釋一下 NPCG 是什麼,因為似乎即使是一些有經驗的正則表達式用戶也沒有完全了解或理解這個概念。假設您已經熟悉捕獲和非捕獲括號的概念(如果您需要復習,請參閱此頁面),請注意 NPCG 與捕獲零長度值(即空字符串)的組不同。這可能是通過展示一些例子最容易解釋的......

以下正則表達式都可能包含 NPCG(取決於它們運行的數據),因為捕獲組不需要參與:

  • /(x)?/
  • /(x)*/
  • /(x){0,2}/
  • /(x)|(y)/ — 如果匹配,則保證只包含一個 NPCG。
  • /(?!(x))/ — 如果匹配(就其本身而言,它至少會在字符串的末尾),則保證包含一個 NPCG,因為該模式僅在“x”匹配失敗時才成功。
  • /()??/ — 這保證在任何字符串 中匹配 包含 NPCG,因為使用了惰性 ?? 零長度值的捕獲組上的量詞。

另一方面,它們永遠不會包含 NPCG,因為雖然它們可以匹配零長度值,但捕獲組是必需的 參加:

  • /(x?)/
  • /(x*)/
  • /(x{0,2})/
  • /((?:xx)?)/ – 或 – /(xx|)/ — 這兩者是等價的。
  • /()?/ – 或 – /(x?)?/ ——這些不是必須參與,而是他們貪婪的? 量詞確保它們總是能成功捕獲至少一個空字符串。

那麼,NPCG 和捕獲空字符串的組有什麼區別?我想這取決於正則表達式庫,但通常情況下,對 NPCG 的反向引用被分配一個特殊的 null 或未定義值。

以下是關於如何在 JavaScript 中處理 NPCG 的 ECMA-262v3 規則(轉述):

  • 在正則表達式中,對 NPCG 的反向引用匹配一個空字符串(即,反向引用總是成功的)。這是不幸的,因為它阻止了一些原本可能的花哨模式(例如,請參閱我的模仿條件的方法),並且與包括 Perl 5 在內的許多其他正則表達式引擎(ECMA 標準正則表達式據說基於)、PCRE、.NET、Java、Python、Ruby、JGsoft 等。
  • 在替換字符串中,對 NPCG 的反向引用會產生一個空字符串(即什麼都沒有)。與前一點不同,這在其他地方很典型,並且允許您使用像 /a(b)|c(d)/ 這樣的正則表達式 並將其替換為 "$1$2" 無需擔心關於非參與組的空指針或錯誤。
  • RegExp.prototype.exec 的結果數組中 , String.prototype.match (與非全局正則表達式一起使用時),String.prototype.split ,以及可用於回調函數的參數 String.prototype.replace , NPCG 返回 undefined .這是一個非常合乎邏輯的方法。

參考:ECMA-262v3 sectons 15.5.4.11、15.5.4.14、15.10.2.1、15.10.2.3、15.10.2.8、15.10.2.9。

不幸的是,NPCG 的實際瀏覽器處理無處不在,導致許多跨瀏覽器差異,如果您不知道自己在做什麼,很容易導致代碼中出現細微(或不那麼細微)的錯誤。例如,Firefox 錯誤地使用帶有 replace() 的空字符串 和 split() 方法,但正確使用 undefined 使用 exec() 方法。相反,IE 正確使用 undefined 使用 replace() 方法,錯誤地使用帶有 exec() 的空字符串 方法,並且不正確地返回 split() 方法,因為它不會將反向引用拼接到結果數組中。至於對非參與組的反向引用的處理within 正則表達式(例如,/(x)?\1y/.test("y") ),Safari 使用更明智、不符合 ECMA 的方法(返回 false 對於前面的代碼),而 IE、Firefox 和 Opera 遵循標準。 (如果您使用 /(x?)\1y/.test("y") 相反,所有四個瀏覽器都將正確返回 true .)

有幾次我看到人們遇到這些差異並錯誤地診斷它們,而不了解根本原因。最近的一個例子是促成這篇文章的原因。

以下是當 NPCG 對結果產生影響時,每種正則表達式和正則表達式使用方法的跨瀏覽器結果:

代碼 ECMA-262v3 IE 5.5 – 7 火狐2.0.0.6 Opera 9.23 Safari 3.0.3
/(x)?\1y/.test("y") true true true true false
/(x)?\1y/.exec("y") ["y", undefined] ["y", ""] ["y", undefined] ["y", undefined] null
/(x)?y/.exec("y") ["y", undefined] ["y", ""] ["y", undefined] ["y", undefined] ["y", undefined]
"y".match(/(x)?\1y/) ["y", undefined] ["y", ""] ["y", undefined] ["y", undefined] null
"y".match(/(x)?y/) ["y", undefined] ["y", ""] ["y", undefined] ["y", undefined] ["y", undefined]
"y".match(/(x)?\1y/g) ["y"] ["y"] ["y"] ["y"] null
"y".split(/(x)?\1y/) ["", undefined, ""] [ ] ["", "", ""] ["", undefined, ""] ["y"]
"y".split(/(x)?y/) ["", undefined, ""] [ ] ["", "", ""] ["", undefined, ""] ["", ""]
"y".search(/(x)?\1y/) 0 0 0 0 -1
"y".replace(/(x)?\1y/, "z") "z" "z" "z" "z" "y"
"y".replace(/(x)?y/, "$1") "" "" "" "" ""
"y".replace(/(x)?\1y/,
    function($0, $1){
        return String($1);
    })
"undefined" "undefined" "" "undefined" "y"
"y".replace(/(x)?y/,
    function($0, $1){
        return String($1);
    })
"undefined" "undefined" "" "undefined" ""
"y".replace(/(x)?y/,
    function($0, $1){
        return $1;
    })
"undefined" "" "" "undefined" ""

(在瀏覽器中運行測試。)

這種混亂的解決方法是避免為未參與的捕獲組創造任何可能,除非您確切知道自己在做什麼。儘管這不是必需的,但 NPCG 通常很容易避免。請參閱本文頂部附近的示例。

編輯(2007-08-16): 我已經用所列瀏覽器的最新版本中的數據更新了這篇文章。原始數據包含一些 Opera 和 Safari 的誤報,這是由於用於生成結果的庫有問題。


Tutorial JavaScript 教程
  1. 使用 LocalStorage 和 IndexedDB 在頁面重新加載之間保持 Vuex 狀態

  2. Javascript decodeURI(Component) 格式錯誤的 uri 異常

  3. Javascript如何過濾嵌套數組數據

  4. 如何在您的項目中使用 Jsonp。

  5. JavaScript:將對像數組映射到字典

  6. 安裝和設置 Gatsby

  7. 在 React Hooks 中思考:為什麼以及何時

  1. 使用保留字作為屬性名,重溫

  2. 🎯 JS 面試清單 - 第 2 部分(高級)

  3. 三個月內學習編碼的 3 種方法

  4. .forEach(), .map(), .filter() .... 有什麼區別?

  5. 這些天你去州管理圖書館做什麼?

  6. Firebase V9 中的 CRUD 操作

  7. 更改內容時的 FadeInLeft 效果

  1. React-Native 下拉搜索欄

  2. 使用 VSCode 成為高效的程序員

  3. Nuxt.js 入門

  4. 無服務器如何節省我的取暖費