第四章 正則表達式回溯法原理

2021-02-21 程序猿DD

第四章 正則表達式回溯法原理

學習正則表達式,是需要懂點兒匹配原理的。

而研究匹配原理時,有兩個字出現的頻率比較高:「回溯」。

聽起來挺高大上,確實還有很多人對此不明不白的。

因此,本章就簡單扼要地說清楚回溯到底是什麼東西。

內容包括:

沒有回溯的匹配

有回溯的匹配

常見的回溯形式

1. 沒有回溯的匹配

假設我們的正則是 /ab{1,3}c/,其可視化形式是:

而當目標字符串是"abbbc"時,就沒有所謂的「回溯」。其匹配過程是:

其中子表達式 b{1,3}表示「b」字符連續出現1到3次。

2. 有回溯的匹配

如果目標字符串是"abbc",中間就有回溯。

圖中第5步有紅顏色,表示匹配不成功。此時 b{1,3}已經匹配到了2個字符「b」,準備嘗試第三個時,結果發現接下來的字符是「c」。那麼就認為 b{1,3}就已經匹配完畢。然後狀態又回到之前的狀態(即第6步,與第4步一樣),最後再用子表達式 c,去匹配字符「c」。當然,此時整個表達式匹配成功了。

圖中的第6步,就是「回溯」。

你可能對此沒有感覺,這裡我們再舉一個例子。正則是:

目標字符串是"abbbc",匹配過程是:

其中第7步和第10步是回溯。第7步與第4步一樣,此時 b{1,3}匹配了兩個"b",而第10步與第3步一樣,此時 b{1,3}只匹配了一個"b",這也是 b{1,3}的最終匹配結果。

這裡再看一個清晰的回溯,正則是:

目標字符串是:"acd"ef,匹配過程是:

圖中省略了嘗試匹配雙引號失敗的過程。可以看出 .*是非常影響效率的。

為了減少一些不必要的回溯,可以把正則修改為 /"[^"]*"/。

3. 常見的回溯形式

正則表達式匹配字符串的這種方式,有個學名,叫回溯法。

回溯法也稱試探法,它的基本思想是:從問題的某一種狀態(初始狀態)出發,搜索從這種狀態出發所能達到的所有「狀態」,當一條路走到「盡頭」的時候(不能再前進),再後退一步或若干步,從另一種可能「狀態」出發,繼續搜索,直到所有的「路徑」(狀態)都試探過。這種不斷「前進」、不斷「回溯」尋找解的方法,就稱作「回溯法」。(copy於百度百科)。

本質上就是深度優先搜索算法。其中退到之前的某一步這一過程,我們稱為「回溯」。從上面的描述過程中,可以看出,路走不通時,就會發生「回溯」。即,嘗試匹配失敗時,接下來的一步通常就是回溯。

道理,我們是懂了。那麼JS中正則表達式會產生回溯的地方都有哪些呢?

3.1 貪婪量詞

之前的例子都是貪婪量詞相關的。比如 b{1,3},因為其是貪婪的,嘗試可能的順序是從多往少的方向去嘗試。首先會嘗試"bbb",然後再看整個正則是否能匹配。不能匹配時,吐出一個"b",即在"bb"的基礎上,再繼續嘗試。如果還不行,再吐出一個,再試。如果還不行呢?只能說明匹配失敗了。

雖然局部匹配是貪婪的,但也要滿足整體能正確匹配。否則,皮之不存,毛將焉附?

此時我們不禁會問,如果當多個貪婪量詞挨著存在,並相互有衝突時,此時會是怎樣?

答案是,先下手為強!因為深度優先搜索。測試如下:

var string = "12345";

var regex = /(\d{1,3})(\d{1,3})/;

console.log( string.match(regex) );

// => ["12345", "123", "45", index: 0, input: "12345"]

其中,前面的 \d{1,3}匹配的是"123",後面的 \d{1,3}匹配的是"45"。

3.2 惰性量詞

惰性量詞就是在貪婪量詞後面加個問號。表示儘可能少的匹配,比如:

var string = "12345";

var regex = /(\d{1,3}?)(\d{1,3})/;

console.log( string.match(regex) );

// => ["1234", "1", "234", index: 0, input: "12345"]

其中 \d{1,3}?只匹配到一個字符"1",而後面的 \d{1,3}匹配了"234"。

雖然惰性量詞不貪,但也會有回溯的現象。比如正則是:

目標字符串是"12345",匹配過程是:

知道你不貪、很知足,但是為了整體匹配成,沒辦法,也只能給你多塞點了。因此最後 \d{1,3}?匹配的字符是"12",是兩個數字,而不是一個。

3.3 分支結構

我們知道分支也是惰性的,比如 /can|candy/,去匹配字符串"candy",得到的結果是"can",因為分支會一個一個嘗試,如果前面的滿足了,後面就不會再試驗了。

分支結構,可能前面的子模式會形成了局部匹配,如果接下來表達式整體不匹配時,仍會繼續嘗試剩下的分支。這種嘗試也可以看成一種回溯。

比如正則:

目標字符串是"candy",匹配過程:

上面第5步,雖然沒有回到之前的狀態,但仍然回到了分支結構,嘗試下一種可能。所以,可以認為它是一種回溯的。

小結

其實回溯法,很容易掌握的。

簡單總結就是,正因為有多種可能,所以要一個一個試。直到,要麼到某一步時,整體匹配成功了;要麼最後都試完後,發現整體匹配不成功。

貪婪量詞「試」的策略是:買衣服砍價。價錢太高了,便宜點,不行,再便宜點。

惰性量詞「試」的策略是:賣東西加價。給少了,再多給點行不,還有點少啊,再給點。

分支結構「試」的策略是:貨比三家。這家不行,換一家吧,還不行,再換。

既然有回溯的過程,那麼匹配效率肯定低一些。相對誰呢?相對那些DFA引擎。

而JS的正則引擎是NFA,NFA是「非確定型有限自動機」的簡寫。

大部分語言中的正則都是NFA,為啥它這麼流行呢?

答:你別看我匹配慢,但是我編譯快啊,而且我還有趣哦。

作者:老姚

原文:https://juejin.im/post/5965943ff265da6c30653879

本文版權歸作者所有,轉載請經得作者授權

第一章 正則表達式字符匹配攻略

第二章 正則表達式位置匹配攻略

第三章 正則表達式括號的作用

相關焦點

  • 【第977期】正則表達式回溯法原理
    正文從這開始~學習正則表達式,是需要懂點兒匹配原理的。而研究匹配原理時,有兩個字出現的頻率比較高:「回溯」。聽起來挺高大上,確實還有很多人對此不明不白的。因此,本文就簡單扼要地說清楚回溯到底是什麼東西。內容包括:1. 沒有回溯的匹配2. 有回溯的匹配3.
  • Python正則表達式入門到入魔
    2、20世紀50年代一位名叫Stephen Kleene的數學科學家發表了一篇題目是《神經網事件的表示法》的論文,利用稱之為正則集合的數學符號來描述此模型,引入了正則表達式的概念。正則表達式被作為用來描述其稱之為「正則集的代數」的一種表達式,因而採用了「正則表達式」這個術語。
  • 正則表達式在VBA中間是如何應用?正則表達式的實現方式?
    Hi,大家好,本章節開始將會從零開始和大家用圖文的方式,讓你從零基礎學會正則表達式!有興趣的小夥伴可以持續關注我,或者在專欄中進行查看自我學習,願與君攜手前行!在上一個章節說到正則表達式的入門級知識點,本節將會與大家分享一下正則表達式的是具體實現方式是怎麼樣的?
  • php正則表達式基本知識與應用詳解
    常用的語言基本上都有正則表達式,如JavaScript、Java等。其實,只有了解一種語言的正則使用,其他語言的正則使用起來,就相對簡單些。文本主要圍繞解決下面問題展開。>⑫ 正則表達式之回溯與固態分組⑬ 正則優缺點有哪些正則表達式的基本知識匯總行定位符(^與$)行定位符是用來描述字符串的邊界。
  • 看完你就會正則表達式了
    最近看了一篇關於正則表達式的學習筆記,覺得講的非常好,更有圖形化的神器相助,想不學會都難,所以想轉給大家看看。話說不是開發為啥要學正則表達式這種看似很晦澀的東西呢,因為現在很多搜索的場景都是支持正則表達式的,學會了正則表達式就有如一把利劍在手。本文較長,建議抽40分鐘完整的時間一次讀完再慢慢消化。
  • 刨根究底正則表達式(1):開篇
    正則表達式目前市面上並不缺乏專業著作,比如那本被譽為正則表達式學習聖經的《精通正則表達式》就很值得一讀,另外該書的譯者餘晟先生所寫的《正則指引》也不錯。如果僅用於入門,則《正則表達式必知必會》肯定不能錯過,還有網上流傳極廣的《正則表達式30分鐘入門教程》也是不錯的入門資料。但是,結合我自身痛苦的正則表達式學習經歷和運用體會,僅有這些是遠遠不夠的。
  • Matlab 正則表達式
    正則表達式是一個非常重要的編程概念,主流的程式語言都對正則表達式進行了很好的支持,Matlab也不例外。本期推文就讓我們來總結一下Matlab提供的正則表達式吧!3 字符串匹配3.1 多次匹配元字符及其特殊字符的匹配,每次只能匹配一個字符,如果需要匹配多個字符,即字符串的匹配,那麼就要重複好幾次元字符的表達式。比如,匹配 mmm,那麼就可以用正則表達式 mmm, 但還有一種更簡單的表示法 m{3}。
  • python正則表達式
    微信公眾號:學點啥玩點啥小白友好型python正則表達式 1#第7章 模式匹配與正則表達式
  • Python正則表達式總結
    正則表達式 的起源、發展、流派、語法、引擎、優化等相關知識,今天我們主要來學習一下 正則表達式在 Python語言 中的應用!9.TEMPLATE語法: re.TEMPLATE  或簡寫為 re.T作用: 豬哥也沒搞懂TEMPLATE的具體用處,源碼注釋中寫著:disable backtracking(禁用回溯),有了解的同學可以留言告知!10.
  • Java正則表達式基礎知識--5個學習步驟
    了解正則表達式概念及基本構造元素(本文目標);2. 通過練習能夠編寫出準確的可讀性較好的正則表達式,熟悉常用正則表達式;3. 理解正則引擎的內部機制及實現,能夠分析正則表達式的效率;4. 能夠編寫效率更高的正則表達式(以準確性和可讀性為前提);5. 能夠運用正則表達式去處理複雜的文本處理問題。1.
  • 給JAVA程式設計師的正則表達式一課
    「不會正則表達式,就算寫遍代碼也嘛不是」。說到正則表達式,可能動態語言的碼農Perl,Python,JS甚至是Golang的開發人員可能都熟悉。對Java碼農來說,可能CURD手到擒來,Spring Stuts Hibernat耳聞能詳,但是說到Regex RE模式,可能熟練的少。那麼,今天蟲蟲就來給廣大Java碼農來補補正則的課。
  • JavaScript高級什麼是正則以及正則表達式的簡單運用
    在實際開發中 ,經常會用到一些表單的驗證 ,提交表單的時候一般都會預校驗 ,比如手機號填寫是否合格 ,用戶暱稱填寫是否規範等 ,這些就要用到正則表達式什麼是正則表達式?用於匹配規律規則的表達式,正則表達式最初是科學家對人類神經系統的工作原理的早期研究,現在在程式語言中有廣泛的應用。正則表通常被用來檢索、替換那些符合某個模式(規則)的文本。
  • VBS正則表達式
    正則表達式作為一個模板,將某個字符模式與所搜索的字符串進行匹配。這裡有一些可能會遇到的正則表達式示例:Visual Basic VBscript. 匹配scripting Edition/^\[ \t]*$/ "^\[ \t]*$" 匹配一個空白行。
  • 教程精選:正則表達式快速入門<二>
    在上篇文章裡,我們介紹了正則表達式的模式修正符與元字符,細心的讀者也許會發現,這部分介紹的非常簡略,而且很少有實際的例子的講解。這主要是因為網上現有的正則表達式資料都對這部分都有詳細的介紹和眾多的例子,如果覺得對前一部分缺乏了解可以參看這些資料。本文希望可以儘可能多涉及一些較高級的正則表達式特性。
  • 正則表達式
    在我看來,正則表達式的主要用途有兩種:①查找特定的信息②查找並編輯特定的信息,也就是我們經常用的替換。。比如我們要在Word,記事本等裡面使用快捷鍵Ctrl+F,進行查找一個特定的字符,或者替換一個字符,這就使用了正則表達式。         正則表達式的功能非常強大,尤其是在文本數據進行處理中顯得更加突出。
  • Python正則表達式,這一篇就夠了!
    大多數程式語言的正則表達式設計都師從Perl,所以語法基本相似,不同的是每種語言都有自己的函數去支持正則,今天我們就來學習 Python中關於 正則表達式的函數。re模塊主要定義了9個常量、12個函數、1個異常,每個常量和函數豬哥都會通過實際代碼案例講解,讓大家能更直觀的了解其作用!註:為避免出現代碼格式錯亂,豬哥儘量使用代碼截圖演示哦。
  • Java 正則表達式教程及示例
    本教程旨在幫助你駕馭Java正則表達式,同時也幫助我複習正則表達式。什麼是正則表達式?正則表達式定義了字符串的模式。正則表達式可以用來搜索、編輯或處理文本。正則表達式並不僅限於某一種語言,但是在每種語言中有細微的差別。Java正則表達式和Perl的是最為相似的。
  • 正則表達式入門教程(下)
    正則表達式裡的分枝條件指的是有幾種規則,如果滿足其中任意一種規則都應該當成匹配,具體方法是用 | 把不同的規則分隔開。聽不明白?如果把它應用於aabab的話,它會匹配aab(第一到第三個字符)和ab(第四到第五個字符)。為什麼第一個匹配是aab(第一到第三個字符)而不是ab(第二到第三個字符)?簡單地說,因為正則表達式有另一條規則,比懶惰/貪婪規則的優先級更高:最先開始的匹配擁有最高的優先權——The match that begins earliest wins。表5.懶惰限定符代碼/語法說明*?
  • Python中的正則表達式
    什麼是正則表達式正則表達式是用於處理字符串的強大工具,它使用預定義的特定模式去匹配一類具有共同特徵的字符串,主要用於快速、準確地完成複雜字符串的查找、替換等。正則表達式進行匹配的流程如下圖所示:正則表達式匹配過程是:依次拿出表達式和文本中的字符比較,如果每一個字符都能匹配,則匹配成功;一旦有匹配不成功的字符則匹配失敗。
  • Python正則表達式急速入門
    正則表達式在程序開發中會經常用到,比如數據(格式)驗證、替換字符內容以及提取字符串內容等等情況都會用到,但是目前許多開發人員對於正則表達式只是處於了解或者是基本會用的階段。一旦遇到大批量使用正則表達式的情況(例如網絡爬蟲)可以說基本上就抓瞎了。這篇文章我將帶領大家利用 Python 來學習一下正則表達式。