<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          能用 AST 搞明白的正則語法,就不需要看文檔

          共 3051字,需瀏覽 7分鐘

           ·

          2022-03-18 06:24

          字符串的處理基本都會用正則表達(dá)式,用它來做字符串的匹配、提取、替換等很方便。

          但是正則表達(dá)式的學(xué)習(xí)還是有些難度的,比如貪婪匹配、非貪婪匹配、捕獲子組、非捕獲子組等概念,不止初學(xué)者難理解,有很多工作幾年的人都不理解。

          那正則表達(dá)式怎么學(xué)比較好?怎么快速掌握正則表達(dá)式呢?

          推薦一個(gè)我覺得很不錯(cuò)的學(xué)習(xí)正則的方式:通過 AST 來學(xué)習(xí)

          正則表達(dá)式的匹配原理是把模式串 parse 成 AST,然后通過這個(gè) AST 去匹配目標(biāo)字符串。

          模式串中的各種信息在 parse 之后都會保存在 AST 里面。AST 是 abstract syntax tree,抽象語法樹的意思,顧名思義,是按照語法結(jié)構(gòu)組織的一棵樹,那么從 AST 的結(jié)構(gòu)上自然可以輕易的知道正則表達(dá)式支持的語法。

          怎么查看正則表達(dá)式的 AST 呢?

          可以通過 astexplorer.net 這個(gè)網(wǎng)站來可視化的查看:

          切換 parse 的語言為 RegExp,就可以做正則表達(dá)式的 AST 的可視化。

          就像前面所說,AST 是按照語法來組織的一棵樹,那么從它的結(jié)構(gòu)上自然能容易地理清各種語法。

          那么我們就從 AST 的角度來學(xué)習(xí)下各種語法吧:

          /abc/

          先從簡單的開始,/abc/ 這樣一個(gè)正則就可以匹配 'abc' 的字符串,它的 AST 是這樣的:

          3 個(gè) Char,值分別是 a、b、c,類型是 simple。那之后的匹配就是遍歷 AST,分別匹配這三個(gè)字符了。

          我們用 exec 的 api 測試了下:

          第 0 個(gè)元素是匹配的字符串,index 是匹配字符串的開始下標(biāo)。input 是輸入的字符串。

          再來試下特殊的字符:

          /\d\d\d/

          /\d\d\d/ 是匹配三個(gè)數(shù)字的意思,\d 是正則支持的有特殊含義的元字符(meta char)。

          通過 AST 我們也可以看出來,它們雖然也是 Char,但類型確是 meta:

          可以通過 \d 的元字符來匹配任意數(shù)字:

          哪些是 meta char 哪些是 simple char,通過 AST 來看一目了然。

          /[abc]/

          正則支持通過 [] 的方式來指定一組字符,也就是說匹配其中任意一種字符都行。

          通過 AST 我們也可以看出來,它被包裹了一層 CharacterClass,就是字符類的意思,也就是匹配它包含的任意一種字符都行。

          測試下也確實(shí)是這樣:

          /a{1,3}/

          正則表達(dá)式支持指定某個(gè)字符重復(fù)多少次,用 {from,to} 的形式,

          比如 /b{1,3}/ 表示字符 b 重復(fù) 1 到 3 次,/[abc]{1,3}/ 表示 a/b/c 這個(gè)字符類重復(fù) 1 到 3 次。

          通過 AST 可以看出來,這種語法叫做 Repetition(重復(fù)):

          他有個(gè) quantifier 的屬性表示量詞,這里的類型是 range,從 1 到 3。

          正則也支持一些量詞的簡寫,比如 + 表示 1 到無數(shù)次、* 表示 0 到無數(shù)次、? 表示 0 或 1 次。

          分別是不同類型的量詞:

          有同學(xué)可能會問,這里的 greedy 屬性是啥意思呢?

          greedy 是貪婪的意思,這個(gè)屬性就表示這個(gè) Repetition 是貪婪匹配還是非貪婪匹配。

          如果在量詞后加個(gè) ?,你就會發(fā)現(xiàn) greedy 變成 false 了,也就是切換到了非貪婪匹配:

          那貪婪和非貪婪是指啥呢?

          我們看個(gè)例子就知道了。

          默認(rèn) Repetition 的匹配是貪婪的,只要滿足條件就一直匹配下去,所以這里 acbac 都能匹配到。

          量詞后加個(gè) ? 就切換到了非貪婪,就只會匹配第一個(gè)了:

          這就是貪婪匹配和非貪婪匹配,通過 AST 我們能夠清楚的知道貪婪和非貪婪是針對重復(fù)語法來說的,默認(rèn)是貪婪匹配,在量詞后加個(gè) ? 就可以切換到非貪婪。

          (aaa)bbb(ccc)

          正則表達(dá)式支持通過()把匹配到的一部分字符串放到子組里返回。

          通過 AST 看一下:

          對應(yīng)的 AST 就叫做 Group。

          而且你會發(fā)現(xiàn)它有個(gè) capturing 的屬性,默認(rèn)是 true:

          這是啥意思呢?

          這就是子組捕獲的語法。

          如果不想捕獲子組,可以這樣寫 (?:aaa)

          看,capturing 變?yōu)?false 了。

          那捕獲和非捕獲有什么區(qū)別呢?

          我們試一下:

          哦,原來 Group 的 capturing 屬性代表的是是否提取的意思啊。

          我們通過 AST 可以看出來,捕獲是針對子組來說的,默認(rèn)是捕獲,也就是提取子組的內(nèi)容,可以通過 ?: 切換到非捕獲,就不會提取子組的內(nèi)容了。

          我們對用 AST 來了解正則語法已經(jīng)輕車熟路了,那來看點(diǎn)難的:

          /bbb(?=ccc)/

          正則表達(dá)式支持通過 (?=xxx) 的語法來表示先行斷言,用來判斷某個(gè)字符串是否前面是某個(gè)字符串。

          通過 AST 可以看到這種語法叫做 Assertion,并且類型為 lookahead,也就是往前看,只匹配前面的意思:

          這是啥意思呢?為啥要這么寫?和 /bbb(ccc)/ 還有 /bbb(?:ccc)/有啥區(qū)別呢?

          我們試一下:

          從結(jié)果可以看出來:

          /bbb(ccc)/ 匹配了 ccc 的子組并且提取出來了這個(gè)子組,因?yàn)槟J(rèn)子組是捕獲的。

          /bbb(?:ccc)/ 匹配了 ccc 的子組但沒有提取出來,因?yàn)槲覀兺ㄟ^ ?: 設(shè)置了子組不捕獲。

          /bbb(?=ccc)/ 匹配了 ccc 的子組也沒有提取出子組,說明也是非捕獲的。它和 ?: 的區(qū)別是 ccc 沒有出現(xiàn)在匹配結(jié)果里。

          這就是先行斷言(lookahead assertion)的性質(zhì):先行斷言代表某段字符串前面是某段字符串,對應(yīng)的子組是非捕獲的,而且斷言的字符串不會出現(xiàn)在匹配結(jié)果中。

          如果后面不是跟著那段字符串就不匹配:

          /bbb(?!ccc)/

          把 ?= 改成 ?! 之后意思就變了,通過 AST 看一下:

          雖然還是先行斷言 lookahead assertion,但是多了個(gè) negative 為 true 的屬性。

          這個(gè)意思很明顯,本來是前面是某段字符串,否定之后就是前面不是某段字符串。

          那匹配結(jié)果正好就反過來了:

          現(xiàn)在前面不是某段字符串的話才匹配了,這就是否定先行斷言。

          /(?<=aaa)bbb/

          有先行斷言自然也有后行斷言,也就是后面是某段字符串才匹配。

          同理,也可以否定:

          (?<=aaa)對應(yīng)的 AST 很容易想到,就是 lookbehind assertion:

          (?

          先行斷言、后行斷言就是最難理解的正則表達(dá)式語法了,通過 AST 來學(xué)習(xí)是不是就容易理解多了~

          總結(jié)

          正則表達(dá)式是處理字符串的很方便的工具,但它的學(xué)習(xí)還是有些難度的,像貪婪匹配、非貪婪匹配、捕獲子組、非捕獲子組、先行斷言、后行斷言等語法很多人都搞不清楚。

          我推薦通過 AST 來學(xué)習(xí)正則,AST 是按照語法結(jié)構(gòu)來組織的一顆對象樹,各種語法通過 AST 節(jié)點(diǎn)的名字和屬性可以輕易的理清楚。

          比如我們通過 AST 理清楚了:

          重復(fù)語法(Repetition)就是字符 + 量詞的形式,默認(rèn)是貪婪匹配(greedy 為 true),代表一直匹配到不匹配為止,量詞后加個(gè) ? 就切換成了非貪婪匹配,匹配到一個(gè)字符就停止。

          子組語法(Group)是用于提取某段字符串的,默認(rèn)是捕獲(capturing 為 true),代表需要提取,可以通過 (?:xxx)切換到非捕獲,只匹配不提取。

          斷言語法(Assertion)代表前面或后面有某段字符串,分為先行斷言(lookahead assertion)和后行斷言(lookbehind assertion),語法分別是(?=xxx)和 (?<=xxx),可以通過把 = 換成 ! 來表示否定(negative 為 true),意思正好反過來。

          是各種文檔對語法理解的深還是編譯器對語法理解的深?

          那還用問,肯定是編譯器呀!

          那么通過它按照語法 parse 出來的語法樹來學(xué)習(xí)語法自然比文檔更好。

          正則表達(dá)式是這樣,其他的語法的學(xué)習(xí)也是這樣,能用 AST 學(xué)會的語法,就不需要看文檔。

          瀏覽 45
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美中日韩 | 欧美自拍视频 在线 | 99精品视频免费看 | 色婷婷精品国产一区二区三区 | 五月天婷婷综合 |