1. 程式人生 > >【JavaScript】正則表示式--非貪婪模式擷取任意字串的筆記

【JavaScript】正則表示式--非貪婪模式擷取任意字串的筆記

場景

要將下面的字串擷取兩個{title:***,url:***}來。

12345<script>{title:'RegExp 物件參考手冊',url:'http://sodino.com/regexp.asp'},{title:'w3cSchool script', url:'http://www.w3cSchool.com'},{}</script>

正則表示式一

12345678910var string = '<script>' +"{title:'RegExp 物件參考手冊',url:'http://sodino.com/regexp.asp'}," +"{title:'w3cSchool script', url:'http://www.w3cSchool.com'},"
+"{}" +'</script>';var reg = /\{.+\}/g; // 正則表示式var result = string.match(reg);console.log(result);

這個毫無疑問是全匹配,即輸出為第一個{到最後一個}之間的全部內容:

12D:\github\niVoVin>node test.js[ '{title:\'RegExp 物件參考手冊\',url:\'http://sodino.com/regexp.asp\'},{title:\'w3cSchool script\', url:\'http://www.w3cSchool.com\'},{}' ]

正則表示式二

書上說在表示式的後面加上?就是非貪婪模式了。即n?匹配包含零個或一個n的字串。
那麼目前想的是隻匹配一個},那麼將表示式修改為如下:

1var reg = /\{.+\}?/g;

執行一下程式碼,發現輸出是竟然是第一個{到字串結尾:

12D:\github\niVoVin>node test.js[ '{title:\'RegExp 物件參考手冊\',url:\'http://sodino.com/regexp.asp\'},{title:\'w3cSchool script\', url:\'http://www.w3cSchool.com\'},{}</script>' ]

?的作用竟然是忽略}

嗎?
陷入了深深的疑惑……

排查問題

當表示式一和表示式二對比時,直覺告訴你是由於引入?才導致的錯誤。
那麼需要來驗證下。

1var reg = /script>?/g;

更換表示式,驗證是否?一樣會忽略掉>???
執行輸出得到:

12D:\github\niVoVin>node test.js[ 'script>', 'script', 'script>' ]

所以?並不是忽略的作用。所以直覺不對。

再看錶達式二的輸出結果,結果是將第一個{後的內容全部輸出,那就是說表示式二的結果是由於一直在匹配.+(任意數量的任意字元,除了換行和結束符),而沒有匹配到截止符}?的原因。
而由於}?處於可匹配可不匹配的情況,直譯器優先選擇不匹配,導致截止符}是無效的。
參考連結正則基礎之——貪婪與非貪婪模式

那麼解決辦法就變為截止符}不可設定為非貪婪,這樣才有截止作用,而對應的是對.+(任意字元)執行非貪婪匹配,儘可能少的匹配{}之間的內容。

正則表示式三

1var reg = /\{.+?\}/g;

輸出結果:

123D:\github\niVoVin>node test.js[ '{title:\'RegExp 物件參考手冊\',url:\'http://sodino.com/regexp.asp\'}', '{title:\'w3cSchool script\', url:\'http://www.w3cSchool.com\'}' ]

很圓滿。

由於.++表示最少出現一次.,所以第三行的空{}並沒有被匹配成功。

結論

在表示式三中,.+表達任意字串,}是截止符。
當使用n+ n* n?擷取任意字串時,如果待擷取內容包括任意字串,則非貪婪模式應儘可能少的匹配任中間的任意字串,而非儘可能少的匹配截止符。