perlRegex第三周筆記

來源:互聯網
上載者:User

標籤:

正則引擎的分類 正則引擎的分類正則引擎的分類主要分兩種:DFA:egrep、awk、lex、flexNFA:.NET、PHP、Perl、Ruby、Python、GNU Emacs、ed、sec、vi、grep等NFA的曆史比DFA久一點,但兩種引擎都發展了20多年,產生了很多變體,POSIX的出現就是為了規範這種現象。POSIX不但規定了元字元的特性,而且規定了Regex應該用什麼樣的方式運作。DFA符合POSIX的標準,但NFA如果要符合POSIX標準,就要作出相應的修改所以引擎可分為:DFA、傳統型NFA、POSIXNFA測試引擎的類型工具所採用的引擎的類型,決定了引擎能夠支援的特性以及這些特性的用途和使用方式。傳統型NFA是使用最廣泛的引擎,而且它很容易識別法一:看看忽略優先量詞是否得到支援,如果是,基本就可以確定是傳統型NFA引擎了法二:用nfa|nfa not來匹配nfa not,如果只有nfa匹配了,就是傳統型NFA,如果整個nfa not都匹配了,要麼是DFA,要麼是POSIX NFADFA和POSIX NFA法一:DFA不支援捕獲型括弧和回測法二:用X(.+)+X來匹配形如=XX=================,如果需要花很長時間,就是NFA,如果時間很短就是DFAAwk、lex、egrep等工具都不支援反向引用和$1功能匹配基礎 規則一規則1:優先選擇最左端的匹配結果因為匹配先從需要尋找的字串的起始位置嘗試匹配,所以起始位置最靠左的匹配結果總是優先於其他可能的匹配結果。用cat來匹配The dragging belly indicates that your cat is too fat.匹配到的是indicates裡面的cat。單詞cat是能被匹配出來的,但是indicates中的cat出現得更早,所以得到的匹配是它。如果引擎不能在字串的開始位置能找到匹配結果,那麼它就會從字串的下一個位置開始嘗試。用fat|cat|belly|your來匹配The dragging belly indicatees that your cat is too fat.得到的匹配結果是belly匹配基礎 規則二規則二:標準量詞是匹配優先的。完成複雜的Regex,我們需要使用星號、加號、問號等元字元。從名字可以看出:標準匹配量詞的結果“可能”不是所有可能匹配中最長的,但它們總是嘗試匹配儘可能多的字元,直到匹配上限為止。舉個例子:用\b\w+s\b匹配包含s的字串,比如regexes,\w+完全能匹配一整個單詞regexes,但是如果\w+匹配了一整個單詞,s就無法匹配了。為了完成匹配,\w+只能匹配regexes,把s\b留出來。用[0-9]+來匹配march 1998中的所有數字,1匹配後,實際上已經滿足了成功匹配的下限,但由於正則引擎匹配優先,所有會繼續匹配998,直到匹配不到連續數字為止。過度的匹配優先匹配優先的特性使得量詞匹配儘可能多的結果,但實際上它的工作原理是這樣的:量詞先匹配盡量多的東西,然後根據Regex其餘的部分按情況“被迫”“交還”出匹配結果。上面的regexes例子就是這樣的。但是交還不能破壞匹配成立的必須條件,比如加號的第一次匹配。再來看看用^.*([0-9][0-9])匹配about 24 char long的過程:. 匹配了一整個字串以後,第一個[0-9]要求必須匹配一個數字,所以.被迫交出最後一個字元g,但是這不能讓[0-9]匹配,所以. 繼續交還,重複幾次以後,交換的字元是4,這下能夠匹配了。但是由於第二個[0-9]也要求匹配一個數字,所以,.又被迫交還一個字元2,兩個[0-9]都匹配成功了,所以.*實際上匹配了about。如果用^. [0-9]+來匹配copyright 2003,那麼只有3會被[0-9]+匹配到,因為當有兩個量詞時,採取先到先得的規則,.被迫交還一個字元就能滿足後面的+。所以0不會再被交還。NFA引擎 運算式主導我們用to (nite|knight|night)來匹配’tonight’。Regex從t開始,每次檢查一個字元,如果能夠匹配,則開始匹配下一個字元。當遇到多選結構時,NFA的引擎會把nite、knight、night分別當作三個不同的獨立的整體去進行匹配。如果nite匹配不成功,則嘗試knight,如果還不成功,最後再嘗試night。運算式的聚焦點從nite到knight再到night,是有運算式的元素控制的,所以這樣的匹配方法叫做運算式主導。DFA引擎 文本主導與NFA不同,DFA在掃描字串時,會記錄“當前有效”的所有匹配可能而考察的下一個文本字元只會在“當前有效”的匹配可能中繼續匹配,至於那些已經無效的匹配可能,就不去管它們。

Tonight
To (nite|knight|night)

這種方式稱為文本主導,因為文本的每一個字元都在控制著引擎的行為。比較NFA與DFA一般情況下,文本主導的DFA引擎要快些,因為運算式主導的NFA引擎需要對同樣的文本進行不同的子運算式的嘗試,例如前面的例子,有三個多選分支,night這個文本就被重複進行匹配了三次,比較浪費時間。另外,NFA引擎必須把整個Regex都經曆完(一直到Regex的最後末尾),才知道匹配的結果。而DFA引擎的原理允許在匹配到沒有“有效”的狀態時,就可以知道全域匹配的結果了。但NFA是運算式主導的,所有使用者可以通過修改Regex來提高效率,所以討論NFA引擎是很有趣的一件事。回溯NFA最重要的性質是,它會依次處理各個子運算式或組成的元素,遇到需要在兩個可能成功的可能匹配中,進行選擇時,它會選擇其一,同時記住另外一個,以備稍後可能的需要。需要作出選擇的情形下包括量詞(決定是否嘗試另一次匹配)和多選結構(決定選擇哪個多選分支,留下哪個稍後嘗試)。不論選擇哪一個途徑,如果它能匹配成功,而且Regex的餘下部分也成功了,匹配即告完成,如果Regex中餘下的部分最終匹配失敗,引擎會知道需要回溯到之前作出選擇的地方,選擇其他備用的分支繼續嘗試。這樣,引擎最終會嘗試運算式的所有可能的途徑,準確一點來說,是會嘗試到匹配完成之前需要的所有途徑。

Tonight
To (nitee|knight|night)

回溯的兩個要點面對眾多的選擇時,哪個分支應當向首先選擇?多選分支就是從左至右的順序。

如果需要在“進行嘗試”和“逃過嘗試”之間選擇,對於匹配優先量詞,引擎會優先選擇“進行嘗試”,而對於忽略優先量詞,會選擇“跳過嘗試”。

回溯進行時,應該選取哪個儲存的狀態?

距離當前最近儲存的選項就是當本地失敗強制回溯時返回的。其實就是後進先出,這個和電腦的堆結構相像。
如果你在每個岔路都撒一堆麵包屑,那麼如果前面是死路,你只需要沿原路返回,直到找到一堆麵包屑為止。

備用狀態

用NFA的術語來說,這些麵包屑就是備用狀態,他們用來標記:在需要的時候,匹配可以從這裡重新開始嘗試。他們儲存了兩個位置:Regex中的位置,和未嘗試的分支在字串中的位置。

未進行回溯的匹配:

用ab?c匹配abc

進行了回溯的匹配:

用ab?c匹配ac

不成功的匹配:

用ab?c匹配abX

忽略優先匹配:

用ab??C匹配abc

回溯和匹配優先在回溯的例子中,我們已經看到了?匹配優先和??忽略優先是怎麼工作的了,現在來看看星號和加號。如果認為x*和x?X?X?X?X?……基本等同,那麼分析的過程和剛才就幾乎是一模一樣的,只不過重複了很多遍而已。用[0-9]+去匹配a 1234 num,匹配完4以後,此時+號可以回溯的備選狀態有四個:

1 234
12 34
123 4
1234

因為[0-9]+相當於[0-9][0-9]?[0-9]?………………使用忽略優先量詞NFA支援忽略優先量詞 ?就是與對應的忽略優先量詞。+?就是與+對應的忽略優先量詞。??就是與?對應的忽略優先量詞。

.*?匹配Billionsand millions of

匹配優先、忽略優先和回溯的要旨

無論是匹配優先,還是忽略優先,都是為全域服務的,如果全域需要,這兩種優先方式遇到“本地匹配失敗”時,引擎都會迴歸到備選狀態(找回麵包屑),然後嘗試尚未嘗試的路徑。所以無論是匹配優先還是忽略優先,只要引擎報告匹配失敗,他必然已經嘗試了所有可能。測試路徑的順序對於兩種優先方式是不同的,但只有在測試了所有可能的路徑以後,才會最終報告匹配失敗。
如果匹配的結果是唯一的(路徑只有一條),那麼使用匹配優先還是忽略優先都能找到這個唯一的結果,只不過是測試的次序不同而已。
如果存在不止一個匹配結果:
The name “McDonald’s” is said “makudonarudo” in Japense.
.*匹配最長的結果,.*?匹配最短的結果

多選結構Perl、PHP、Java、.NET以及其他語言使用的NFA引擎,遇到多選結構時,都是按照從左至右的順序檢查運算式的多選分支。如用subject|date,會先使用subject,如果能夠匹配,就繼續進行下去,不會再管date,如果不匹配,則回溯並且使用date,所以正則引擎會回溯到存在尚未嘗試嘗試的多選分支的地方。

當需要匹配Jan 31這樣的日期時,我們需要的不是簡單的Jan [0123][0-9],因為這樣有可能會匹配到Jan 00或Jan 39,而且無法匹配Jan 7這樣的日期。

一種方法是把日期拆開,用0?[1-9]匹配可能用0開頭的前九天的日期,用[12][0-9]處理十號到二十九號,用3[01]處理最後兩天。可是我們應該注意多選結構的順序,如果用Jan (0?[1-9]|[12][0-9]|3[01])來匹配Jan 31,只會得到Jan 3,因為在第一個多選分支可以成功匹配3.所以我們把能夠匹配的最短的數字放在最後面,問題就解決了:Jan ([12][0-9]|3[01]|0?[1-9])匹配IP地址匹配一個ip地址,用點號隔開四個數,例如001.002.003.004

如果用[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*來匹配顯然這個運算式不夠精緻,它甚至可以只匹配三個點…。
第二個想法是把*換成+,這樣就能確保有數字了,[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+,當然這樣的Regex依然不符合要求,因為他會匹配到1234.5678.91234.3215600564這樣的組合。
我們應該限定點號之間只能有1個或2個或3個數字,對於支援區間量詞的流派,可以用\d{1,3}\.\d{1,3}\.\d{1,3}來匹配,對於不支援區間量詞的,可以用\d\d?\d?或者\d(\d?\d)?來替代。
上面的運算式確實可以匹配ip地址了,但現在我們還要進一步,匹配有效IP地址,該怎
麼辦呢?
我們關注欄位中什麼位置可以出現那些數字。IP地址的三位元是不超過255的,所以如果
一個欄位只包含一個或者兩個數字,就無需擔心這個欄位的值是否合法,因為一定是合法
的,這個用\d|\d\d就能應付。同樣,我們也不必擔心以0或者1開頭的三位元,因為000-
199都是合法的,所以現在我們的運算式變成了\d|\d\d|[01]\d\d
如果以2開頭的三位元字,小於255就是合法的,所以第二位元小於5就代表整個欄位合法
,如果第二位是5,則第三位小於6就整個欄位合法。這可以表示為2[0-4]\d|25[0-5]
現在我們的運算式就是\d|\d\d|[01]\d\d| 2[0-4]\d|25[0-5] ,然後還可以把前面三個分支
簡化成[01]?\d\d?| 2[0-4]\d|25[0-5]
現在是可以匹配一個精準的IP地址了。

處理檔案名稱

去掉檔案名稱開頭的路徑:
/user/local/bin/gcc變成gcc,我們就可以用.*匹配優先的特性,使得.*匹配一整個路徑
,然後再加上/,讓Regex回溯到最後一個斜杠,也就是逼迫.*交還字元直到遇到最後
一個斜杠。 具體的做法 =~s{^.*/}{}
從路徑中擷取檔案名稱:
[^/]*$來從路徑中擷取最後的檔案名稱。
可是大家如果完全明代前面我們說的內容,就會發現這個運算式包括了太多回溯。即使是
短短的/user/local/bin/gcc,在獲得匹配結果前,也經曆了40多次回溯。
分隔路徑和檔案名稱:
^(.*)/(.*)$來分隔最後一個斜杠之前的和之後的內容。
如果更加精確一點,可以使用^(.*)/([^/]*)$

匹配對稱的括弧

為了匹配val=foo(bar(this),3.7)+2*(that-1);中的(bar(this),3.7)

  1. \(.*\)
  2. \([^)]*\)
  3. \([^()]*\)
    第一個匹配的太長,第二個匹配的太短,第三個只能匹配(this)
    Regex無法匹配任意深度的嵌套結構
    但是可以匹配特定深度的嵌套括弧
匹配HTML tag

最常見的方法是用<[^>]+>來匹配HTML標籤。這通常都能成功。
但是如果標籤中又含有>,例如<input name=“dir”value=“>” >這樣子就匹配不成
功。
熟悉HTML的同學會發現,=號後面必須會出現單引號或者雙引號,所以我們只需要把<>
中的內容分成引用根本和非引用文本,就可以完成匹配所有情況的標籤。

<(“[^”]”|’[^’]’|[^”’>])*>
匹配HTML link

在HTML中,<a href=“http://www.oreilly.com”>O’Reilly</a>
我們用<a\b([^>]+)>(.*?)</a>來分別提取連結和連結文本
然後$1=~m{href\s*=\s*(?:”([^”]*)”|’([^’]*)’|([^’”>\s]+))}xi
然後用$+這個變數,這個變數儲存的是$1、$2等數字變數中編號最靠後的變數。
這裡就是我們想要的URL。

校正HTTP URL

現在我們得到了URL地址,來看看他是否是HTTP URL,如果是,就把它分解為主機名稱和
路徑兩部分。
主機名稱是^http://之後和第一個反斜線(如果有的話)之間的內容,而路徑就是除此之外
的內容,而路徑就是除此之外的內容:^http://([^/]+)(/.*)?$
URL中有可能包含連接埠號碼,它位於主機名稱和路徑之間,以一個冒號開頭

^http://([^/:]+)(:(\d+))?(/.*)?$

perlRegex第三周筆記

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.