[譯] 即將到來的正規表示式新特性

磊仔發表於2017-07-14

ES2015 給 JavaScript 語言引入了許多新特性,其中包括正規表示式語法的一些重大改進,新增了 Unicode 編碼 (/u) 和粘滯位 (/y)兩個修飾符。而在那之後,發展也並未停止。經過與 TC39(ECMAScript 標準委員會)的其他成員的緊密合作,V8 團隊提議並共同設計了讓正規表示式更強大的幾個新特性。

這些新特性目前已經計劃包含在 JavaScript 標準中。雖然提案還沒有完全通過,但是它們已經進入 TC39 流程的候選階段了。我們已經以試驗功能(見下文)在瀏覽器實現了這些特性,以便在最終定稿之前提供及時的設計和實現反饋給各自的提案作者。

本文給您預覽一下這個令人興奮的未來。如果您願意跟著體驗這些即將到來的示例,可以在 chrome://flags/#enable-javascript-harmony 頁面中開啟實驗性 JavaScript 功能。

命名捕獲

正規表示式可以包含所謂的捕獲(或捕獲組),它可以捕獲一部分匹配的文字。到目前為止,開發者只能通過索引來引用這些捕獲,這取決於其在正則匹配中的位置。

const pattern =/(\d{4})-(\d{2})-(\d{2})/u;
const result = pattern.exec('2017-07-10');
// result[0] === '2017-07-10'
// result[1] === '2017'
// result[2] === '07'
// result[3] === '10'複製程式碼

但正規表示式已經因難於讀、寫和維護而臭名昭著,並且數字引用會使事情進一步複雜化。例如,在一個更長的表示式中判斷一個獨特捕獲的索引是很困難的事:

/(?:(.)(.(?<=[^(])(.)))/  // 最後一個捕獲組的索引是?複製程式碼

更糟糕的是,更改一個表示式可能會潛在地轉變所有已存在的捕獲的索引:

/(a)(b)(c)\3\2\1/     // 一些簡單的有序的反向引用。
/(.)(a)(b)(c)\4\3\2/  // 所有都需要更新。複製程式碼

命名捕獲是一個即將到來的特性,它允許開發者給捕獲組分配名稱來幫助儘可能地解決這些問題。語法類似於 Perl、Java、.Net 和 Ruby:

const pattern =/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const result = pattern.exec('2017-07-10');
// result.groups.year === '2017'
// result.groups.month === '07'
// result.groups.day === '10'複製程式碼

命名捕獲組也可以被命名的反向引用來引用,並傳入 String.prototype.replace

// 命名反向引用。
/(?<LowerCaseX>x)y\k<LowerCaseX>/.test('xyx');  //true

// 字串替換。
const pattern =/(?<fst>a)(?<snd>b)/;
'ab'.replace(pattern,'$<snd>$<fst>');                              // 'ba'
'ab'.replace(pattern,(m, p1, p2, o, s,{fst, snd})=> fst + snd);  // 'ba'複製程式碼

關於這個新特性的全部詳情可以在規範提案中檢視。

dotAll 修飾符

預設情況下,元字元 . 在正規表示式中匹配除了換行符以外的任何字元:

/foo.bar/u.test('foo\nbar');   // false複製程式碼

一個提案引入了 dotAll 模式,通過 /s 修飾符來開啟。在 dotAll 模式中,. 也可以匹配換行符。

/foo.bar/su.test('foo\nbar');  // true複製程式碼

關於這個新特性的全部詳情可以在規範提案中檢視。

Unicode 屬性逃逸(Unicode Property Escapes)

正規表示式語法已經包含了特定字元類的簡寫。\d 代表數字並且只能是 [0-9]\w 是單詞字元的簡寫,或者寫成 [A-Za-z0-9_]

自從 ES2015 引入了 Unicode,突然間大量的字元可以被認為是數字,例如圈一:①;或者被認為是字元的,例如中文字元:雪。

它們都不會被 \d\w 匹配。而改變這些簡寫的含義將會破壞已經存在的正規表示式模式。

於是,新的字串類被引入。注意它們只在使用 /u 修飾符的 Unicode-aware 正規表示式中可用。

/\p{Number}/u.test('①');      // true
/\p{Alphabetic}/u.test('雪');  // true複製程式碼

排除型字元可以使用 \P 匹配。

/\P{Number}/u.test('①');      // false
/\P{Alphabetic}/u.test('雪');  // false複製程式碼

統一碼聯盟還定義了許多方式來分類碼位,例如數學符號和日語平假名字元:

/^\p{Math}+$/u.test('∛∞∉');                            // true
/^\p{Script_Extensions=Hiragana}+$/u.test('ひらがな');  // true複製程式碼

全部受支援的 Unicode 屬性類列表可以在目前的規範提案中找到。更多示例請檢視這篇內容豐富的文章

後行斷言

先行斷言從一開始就已經是 JavaScript 正規表示式語法的一部分。與之相對的後行斷言也終於將被引入。你們中的一些人可能記得,這成為 V8 的一部分已經有一段時間了。我們甚至在底層已經用後行斷言實現了 ES2015 規定的 Unicode 修飾符。

“後行斷言”這個名字已經很好地描述了它的涵義。它提供一個方式來限制一個正則,只有後行組匹配通過之後才繼續匹配。它提供匹配和非匹配兩種選擇:

/(?<=\$)\d+/.exec('$1 is worth about ¥123');  // ['1']
/(?<!\$)\d+/.exec('$1 is worth about ¥123');  //['123']複製程式碼

更多詳細資訊,檢視我們之前的一篇博文,專門介紹了後行斷言。相關示例可以檢視V8 測試用例

致謝

本文的完成有幸得到了很多相關人士的幫助,他們的辛勤工作造就了這一切:特別是語言之王Mathias BynensDan EhrenbergClaude PacheBrian TerlsonThomas Wood、Gorkem Yakin、和正則大師 Erik Corry;還有為語言規範作出努力的每一個人以及 V8 團隊對這些特性的實施。

希望您能像我們一樣為這些新的正規表示式特性而感到興奮!


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃

相關文章