正則表達式高級進階

概述

本文主要經過介紹正則表達式中的一些進階內容,讓讀者瞭解正則表達式在平常使用中用到的比較少可是又比較重要的一部份內容,從而讓你們對正則表達式有一個更加深入的認識。javascript

本文的主要內容爲:java

  • 正則表達式回溯法原理
  • 正則表達式操做符優先級

本文不介紹相關正則表達式的基本用法,若是對正則表達式的基本使用方法還不瞭解的同窗,能夠閱讀個人上一篇博客——正則表達式語法入門正則表達式

回溯法原理

回溯是影響正則表達式效率的一個很是重要的緣由,咱們在進行正則表達式匹配時,必定要儘量的避免回溯。算法

不少人可能只是對據說過「回溯法」,並不瞭解其中具體內容和原理,接下來就先讓咱們看下什麼是回溯法。post

回溯法的定義

回溯法就是指正則表達式從頭開始依次進行匹配,若是匹配到某個特定的狀況下時,發現沒法繼續進行匹配,須要回退到以前匹配的結果,選擇另外一個分支繼續進行匹配中的現象。這個描述可能有點抽象,咱們舉一個簡單的例子,讓你們可以更加明確的理解回溯法:網站

const reg = /ab{1,3}c/;

const str = 'abbc';

// 第1步:匹配/a/,獲得'a'
// 第2步:匹配/ab{1}/,獲得'ab'
// 第3步:匹配/ab{2}/,獲得'abb'
// 第4步:匹配/ab{3}/,匹配失敗,須要進行回溯
// 第5步:回溯到/ab{2}/,繼續匹配/ab{2}c/,獲得'abbc'
// 第6步:正則表達式匹配完成,獲得'abbc'

若是咱們把正則表達式的各個分支都整理成一棵樹的話,正則表達式的匹配其實就是一個深度優先搜索算法。而回溯其實就是在進行深度優先匹配失敗後的後退正常操做邏輯。spa

若是退回到了根節點仍然沒法匹配的話,就會將index向後移動一位,從新構建匹配數。即/bc/'abc'時,因爲第一個字符'a'沒法匹配,則移動到'b'開始匹配。3d

回溯法產生場景

理解了回溯法和回溯操做,接下來咱們來看下什麼場景下會出現回溯。出現回溯的場景主要有如下幾種:code

  1. 貪婪量詞(貪婪匹配)
  2. 惰性量詞(非貪婪匹配)
  3. 分支結構(分支匹配)

接下來,讓咱們一個一個來看下這些場景是如何出現回溯的。blog

貪婪量詞(貪婪匹配)

const reg = /ab{1,3}c/;

const str = 'abbc';

// 第1步:匹配/a/,獲得'a'
// 第2步:匹配/ab{1}/,獲得'ab'
// 第3步:匹配/ab{2}/,獲得'abb'
// 第4步:匹配/ab{3}/,匹配失敗,須要進行回溯
// 第5步:回溯到/ab{2}/,繼續匹配/ab{2}c/,獲得'abbc'
// 第6步:正則表達式匹配完成,獲得'abbc'

最開始的例子其實就是一個貪婪匹配的示例,經過儘量多的匹配b從而致使了回溯。

惰性量詞(非貪婪匹配)

const reg = /ab{1,3}?c/;

const str = 'abbc';

// 第1步:匹配/a/,獲得'a'
// 第2步:匹配/ab{1}/,獲得'ab'
// 第3步:匹配/ab{1}c/,匹配失敗,須要進行回溯
// 第4步:回溯到/ab{1}/,繼續匹配/ab{2}/,獲得'abb'
// 第5步:匹配/ab{2}c/,獲得'abbc'
// 第6步:正則表達式匹配完成,獲得'abbc'

與貪婪匹配相似,非貪婪匹配雖然每次都是去最小匹配數目,可是也會出現回溯的狀況。

分支結構(分支匹配)

const reg = /(ab|abc)d/;

const str = 'abcd';

// 第1步:匹配/ab/,獲得'ab'
// 第2步:匹配/abd/,匹配失敗,須要進行回溯
// 第3步:回溯到//,繼續匹配/abc/,獲得'abc'
// 第4步:匹配/abcd/,獲得'abcd'
// 第5步:正則表達式匹配完成,獲得'abcd'

經過上面的示例咱們能夠看到,分支結構在出現兩個分支狀況相似的時候,也會出現回溯的狀況,在這種狀況下,若是一個分支沒法匹配,則會回到這個分支的最初狀況來從新進行匹配。

正則表達式操做符優先級

看完了回溯法,下面咱們來了解下關於正則表達式操做符的優先級。

咱們直接看結論,而後再根據結論來給你們提供示例進行理解。

操做符描述 操做符 優先級
轉移符 \ 1
小括號和中括號 (…)、(?:…)、(?=…)、(?!…)、[…] 2
量詞限定符 {m}、{m,n}、{m,}、?、*、+ 3
位置和序列 ^、$、元字符、通常字符 4
管道符 \ 5

經過操做符的優先級,咱們可以知道如何來讀一個正則表達式。如下面這個正則表達式爲例,咱們來介紹一下按照優先級進行分析的方法:

const reg = /ab?(c|de*)+|fg/;

// 第一步,根據優先級先考慮(c|de*)+,再根據優先級拆分獲得c de*,即匹配c或者de*(注意,位置和序列的優先級高於管道符|,因此是c或de*而不是c或d和e*)
// 第二步,獲得ab?,根據優先級拆分獲得a和b?
// 第三步,獲得fg,這個內容和第一步+第二步的結果爲或的關係

最終,咱們獲得的效果以下:

clipboard.png

經過這個圖,你們就可以理解咱們的分析思路:先找括號,括號中的必定爲一個總體(轉移符只作轉義,不分割正則,所以能夠認爲第一優先級實際上是括號),沒有括號後再從左到右按照優先級進行分析。量詞限定符則看作是正則的一個總體。

注:若是你們須要話相似的正則表達式流程圖,可使用此網站

根據上面的優先級,咱們就可以避免在正則表達式的理解中出現歸類錯誤的狀況。

總結

本文經過介紹在正則表達式中容易被忽略的兩個內容:回溯法操做優先級,讓你們可以在進行正則的閱讀和書寫過程當中避免踩到相關的坑。

參考內容

  1. 《JavaScript正則表達式迷你書》——老姚 V1.1
  2. 《JavaScript權威指南》

個人博客即將搬運同步至騰訊雲+社區,邀請你們一同入駐:https://cloud.tencent.com/dev...

相關文章
相關標籤/搜索