談談正規表示式中的 “.”

周驊發表於2019-02-27

寫於2015年5月7日,可能已過時,請謹慎參考。

原文

好久沒更新內容了,今天分享一個小的知識點,一個正規表示式方面的很容易被人忽視的坑。

我們知道,正規表示式中,可以用 . 表示任意單個字元,但在 underscore 和 jquery 的原始碼中,我們可以看到,這些著名類庫的程式碼中,經常並不是用 . 來表示任意字元,而是使用 [wW] 或者 [sS]。乍一看,好像表達的含義是一樣的,可是為什麼放著簡單的方法不用,而去多繞個圈子?今天就簡單說說這個問題。

首先我們必須要正確理解 . 的含義。其實說它表示任意單個字元,可能會讓人產生誤解,必須要強調一下,這個“任意單個字元”不包括控制換行的字元,也就是不包含

u2028 或 u2029 這幾個字元。而 W 和 s 中是能夠包含這些字元的。那個這兩種寫法的差異也就很清晰了,就是能否匹配到幾個換行控制符的差異。

那麼在什麼時候我們需要考慮這幾個換行控制符呢?當要處理的字串可能包含換行時。這樣的情景太多了,處理 html 字串、處理 template、Node.js 讀取文字等等。

提及多行文字的場景,我們很容易想到正規表示式的 m 模式(多行模式)。那麼多行模式對我們今天討論的問題有影響嗎?我不是很確定。為什麼不確定呢?有些人信誓旦旦地聲稱單行模式下 . 的含義與多行模式下不同,單選模式下等同於 [wW] 或者 [sS],而多行模式下會排除換行控制符。但據我試驗,以及參考 MDN 的說法,這是不對的。的確有很多語言的正規表示式會有上述特性,但在 javascript 中我沒有看到,不知道會不會有瀏覽器方面的差異。那麼多行模式對於 javascript 而言影響的是什麼呢?我認為僅僅是改變了 ^$ 標識的含義:單行模式下,分別表示整個字串的開始的結尾;多行模式下表示每一行的開始和結尾。而不管多行模式還是單行模式,我認為 . 都是不包含換行控制字元的,等價於 [^

u2028u2029]。

再多延伸一點點,對於現代瀏覽器,可以直接用 [^] 來匹配任意字元的。

例子程式不想寫了,有興趣的可以自己試驗一下,分別用 /.*/g/^.*$/g/.*/gm/^.*$/gm 來匹配一下 "abc
edf"
,其中道理不言自明。


2018年3月16日補充:

感謝 Will_Liu 提醒,ECMAScript中正則增加 dotAll(s) 的標識的提案已經在 stage 4 了。詳情請參考:proposal-regexp-dotall-flag

如果使用 babel,可以新增 @babel/plugin-transform-dotall-regex 開啟這個特性。

示例 .babelrc:

{
  "plugins": ["@babel/plugin-transform-dotall-regex"]
}
複製程式碼

相關文章