国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

進階正則表達式

leo108 / 1499人閱讀

摘要:一正則表達式的工作機制畫了一個草圖,簡單的說明了下正則表達式的工作原理。只要正則表達式沒有嘗試完所有的可選項,他就會回溯到最近的決策點也就是上次匹配成功的位置。而在正則表達中,主要就是之類的數字引用。

本文同步自我的博客園:http://www.cnblogs.com/hustskyking/

關于正則表達式,網上可以搜到一大片文章,我之前也搜集了一些資料,并做了排版整理,可以看這篇文章http://www.cnblogs.com/hustskyking/archive/2013/06/04/RegExp.html,作為基礎入門講解,這篇文章說的十分到位。

記得最開始學習正則,是使用 php 做一個爬蟲程序。為了獲取指定的信息,必須用一定的方式把有規律的數據匹配出來,而正則是首選。下面是當時寫的爬蟲程序的一個代碼片段:

$regdata = "/((?[^<]*)
){0,1}⊙(?.{12})S*s/"; //獲取頁面 $html = file_get_contents("http://www.qnwz.cn/html/daodu/201107/282277.html"); $html = iconv("GBK", "UTF-8", $html); if ($html == "") { die("
出錯:【錯】無法打開《青年文摘》頁面
"); } //匹配頁面信息 preg_match_all($regdata, $html, $mdata); print_r($mdata);

當時寫代碼還真是歡樂多,什么都不懂,什么都是新知識,學起來津津有味。我覺得學習知識一定要把握最基本的原理,先把一個知識的大概輪廓搞清楚,然后學習怎么去使用他,完了就是深入學習,了解底層基礎實現。很多人解決問題都是靠經驗,這個當然很重要,但如果我們弄懂了一項技術最底層的實現,完全可以靠自己的推斷分析出問題的根源。我對一些公司的招聘要求特別不滿,說什么要三年五年Javascript編程經驗云云,經驗當然和時間成正相關,但是對于那些沒有三年五年工作經驗卻照樣能夠解決實際的人呢?算是小小的吐槽吧,下面進入正題。

一、正則表達式的工作機制

畫了一個草圖,簡單的說明了下正則表達式的工作原理。

    +--------+
    |  編譯  |
    +--------+
         |
         ↓
+----------------+
|  設置開始位置   |←---------+
+----------------+          ↑
         |                  |
         ↓               其 |
+----------------+       他 |
|  匹配 & 回溯   |        路 |
+----------------+       徑 |
         |                  |
         ↓                  |
+----------------+          |
|  成功 or 失敗   |---------→+
+----------------+

你寫的任何一個正則直接量或者 RegExp 都會被瀏覽器編譯為一個原生代碼程序,第一次匹配是從頭個字符開始,匹配成功時,他會查看是否還有其他的路徑沒有匹配到,如果有的話,回退到上一次成功匹配的位置,然后重復第二步操作,不過此時開始匹配的位置(lastIndex)是上次成功位置加 1.這樣說有點難以理解,下面寫了一個 demo,這個 demo 就是實現一個正則表達式的解析引擎,因為邏輯和效果的表現都太復雜了,所以只做了一個簡單的演示:

http://qianduannotes.duapp.com/demo/regexp/index.html

如果要深入了解正則表達式的內部原理,必須先理解匹配過程的一個基礎環節——回溯,他是驅動正則的一個基本動力,也是性能消耗、計算消耗的根源。

二、回溯

正則表達式中出現最多的是分支和量詞,上面的 demo 中可以很清楚的看到 hi 和 hello 這兩個分支,當匹配到第一個字符 h 之后,進入 (i|ello) 的分支選擇,首先是進入 i 分支,當 i 分支匹配完了之后,再回到分支選擇的位置,重新選擇分支。簡單點說,分支就是 | 操作符帶來的多項選擇問題,而量詞指的是諸如 *, +?, {m,n} 之類的符號,正則表達式必須決定何時嘗試匹配更多的字符。下面結合回溯詳細說說分支和量詞。

1. 分支

繼續分析上面那個案例。"Lalala. Hi, barret. Hello, John".match(/H(i|ello), barret/g),首先會查找 H 字符,在第九位找到 H 之后,正則子表達式提供了兩個選擇 (i|ello),程序會先拿到最左邊的那個分支,進入分支后,在第十位匹配到了 i,接著匹配下一個字符,下一個字符是逗號,接著剛才的位置又匹配到了這個逗號,然后再匹配下一個,依次類推,直到完整匹配到整個正則的內容,此時程序會在Hi, barret后面做一個標記,表示在這里進行了一次成功的匹配。但程序到此并沒有結束,因為后面加了一個全局參數,依然使用這個分支往后匹配,很顯然,到了 Hello 的時候,Hi 分支匹配不了了,于是程序會回溯到剛才我們做標記的位置,并進入第二個分支,從做標記的位置重新開始匹配,依次循環。

只要正則表達式沒有嘗試完所有的可選項,他就會回溯到最近的決策點(也就是上次匹配成功的位置)。

2. 量詞

量詞這個概念特別簡單,只是在匹配過程中有貪婪匹配和懶惰匹配兩種模式,結合回溯的概念理解稍微復雜。還是用幾個例子來說明。

1) 貪婪

str = "AB1111BA111BA";
reg = /AB[sS]+BA/;
console.log(str.match(reg));

首先是匹配AB,遇到了 [sS]+,這是貪婪模式的匹配,他會一口吞掉后面所有的字符,也就是如果 reg 的內容為 AB[sS]+,那后面的就不用看了,直接全部匹配,而往后看,正則后面還有B字符,所以他會先回溯到倒數第一個字符,匹配看是否為 B,顯然倒數第一個字符不是B,于是他又接著回溯,找到了B字母,找到之后就不繼續回溯了,而是往后繼續匹配,此刻匹配的是字符A,程序發現緊跟B后的字母確實是A,那此時匹配就結束了。如果沒有看明白,可以再讀讀下面這個圖:

  REG: /AB[sS]+BA/
MATCH: A               匹配第一個字符
       AB              匹配第二個字符
       AB1111BA111BA   [sS]+ 貪婪吞并所有字符
       AB1111BA111BA   回溯,匹配字符B
       AB1111BA111B    找到字符B,繼續匹配A
       AB1111BA111BA   找到字符A,匹配完成,停止匹配

2) 懶惰(非貪婪)

str = "AB1111BA111BA";
reg = /AB[sS]+?BA/;
console.log(str.match(reg));

與上面不同的是,reg 中多了一個 ? 號,此時的匹配模式為懶惰模式,也叫做非貪婪匹配。此時的匹配流程是,先匹配AB,遇到[sS]+?,程序嘗試跳過并開始匹配后面的字符B,往后查看的時候,發現是數字1,不是要匹配的內容,繼續往后匹配,知道遇到字符B,然后匹配A,發現緊接著B后面就有一個A,于是宣布匹配完成,停止程序。

  REG: /AB[sS]+BA/
MATCH: A               匹配第一個字符
       AB              匹配第二個字符
       AB              [sS]+? 非貪婪跳過并開始匹配B
       AB1             不是B,回溯,繼續匹配
       AB11            不是B,回溯,繼續匹配
       AB111           不是B,回溯,繼續匹配
       AB1111          不是B,回溯,繼續匹配
       AB1111B         找到字符B,繼續匹配A
       AB1111BA        找到字符A,匹配完成,停止匹配

如果匹配的內容是 AB1111BA,那貪婪和非貪婪方式的正則是等價的,但是內部的匹配原理還是有區別的。為了高效運用正則,必須搞清楚使用正則時會遇到那些性能消耗問題。

三、逗比的程序
//去測試下這句代碼
"TTTTTTTT".match(/(T+T+)+K/);
//然后把前面的T重復次數改成30
//P.S:小心風扇狂轉,CPU暴漲

我們來分析下上面這段代碼,上面使用的都是貪婪模式,那么他會這樣做:

  REG: (T+T+)+K
MATCH: ①第一個T+匹配前7個T,第二個T+匹配最后一個T,沒找到K,宣布失敗,回溯到最開始位置
       ②第一個T+匹配前6個T,第二個T+匹配最后兩個T,沒找到K,宣布失敗,回溯到最開始位置
       ③...
       ... 接著還會考慮(T+T+)+后面的 + 號,接著另一輪的嘗試。
       ⑦...
       ...

這段程序并不會智能的去檢測字符串中是否存在 K,如果匹配失敗,他會選擇其他的匹配方式(路徑)去匹配,從而造成瘋狂的回溯和重新匹配,結果可想而知。這是回溯失控的典型例子。

四、前瞻和反向引用 1. 前瞻和引用

前瞻有兩種,一種是負向前瞻,JS中使用 (?!xxx) 來表示,他的作用是對后面要匹配的內容做一個預判斷,如果后面的內容是xxx,則此段內容匹配失敗,跳過去重新開始匹配。另一種是正向前瞻,(?=xxx),匹配方式和上面相反,還有一個長的類似的是 (?:xxx),這個是匹配xxx,他是非捕獲性分組匹配,即匹配的內容不會創建反向引用。具體內容可以去文章開頭提到的文檔中查看。

反向引用,這個在 replace 中用的比較多,在 replace 中:

字符 替換文本
$1、$2、...、$99 與 regexp 中的第 1 到第 99 個子表達式相匹配的文本。
$& 與 regexp 相匹配的子串。
$` 位于匹配子串左側的文本。
$" 位于匹配子串右側的文本。
$$ 直接量符號。

而在正則表達中,主要就是 1, 2 之類的數字引用。前瞻和反向引用使用恰當可以大大的減少正則對資源的消耗。舉個例子來簡單說明下這幾個東西:

問題:使用正則匹配過濾后綴名為 .css 和 .js 的文件。
      如:test.wow.js test.wow.css test.js.js等等。

有人會立馬想到使用負向前瞻,即:

//過濾js文件
/(?!.+.js$).*/.exec("test.wow.js")

//過濾js和css文件
/(?!.+.js$|.+.css$).*/.exec("test.wow.js")
/(?!.+.js$|.+.css$).*/.exec("test.wow.html")

但是你自己去測試下,拿到的結果是什么。匹配非js和非css文件可以拿到正確的文件名,但是我們期望這個表達式對js和css文件的匹配結果是null,上面的表達式卻做不到。問題是什么,因為(?!xxx)和(?=xxx)都會消耗字符,在做預判斷的時候把 .js 和 .css 給消耗了,所以這里我們必須使用非捕獲模式。

/(?:(?!.+.js$|.+.css$).)*/.exec("test.wow.html");
/(?:(?!.+.js$|.+.css$).)*/.exec("test.wow.js");

我們來分析下這個正則:

(?:(?!.+.js$|.+.css$).)*
---   ----------------  -
 |                |     |   
 +----------------------+
             ↓    | 
非捕獲,內部只有一個占位字符
                  |
                  ↓
    負向前瞻以.js和.css結尾的字符串

最后一個星號是貪婪匹配,直接吞掉全部字符。

這里講的算是有點復雜了,不過在稍復雜的正則中,這些都是很基礎的東西了,想在這方面提高的童鞋可以多研究下。

2. 原子組

JavaScript的正則算是比較弱的,他沒有分組命名、遞歸、原子組等功能特別強的匹配模式,不過我們可以利用一些組合方式達到自己的目的。上面的例子中,我們實際上用正則實現了一個或和與的功能,上面的例子體現的還不是特別明顯,再寫個例子來展示下:

str1 = "我(wo)叫(jiao)李(li)靖(jing)";
str2 = "李(li)靖(jing)我(wo)叫(jiao)";
reg = /(?=.*?我)(?=.*?叫)(?=.*?李)(?=.*?靖)/;
console.log(reg.test(str1)); //true
console.log(reg.test(str2)); //true

不管怎么打亂順序,只要string中包含“我”,“是”,“李”,“靖”這四個字,結果都是true。

類似(?=xxx)1,就相當于一個原子組,原子組的作用就是消除回溯,只要是這種模式匹配過的地方,回溯時都不會到這里和他之前的地方。上面的程序"TTTTTTTT".match(/(T+T+)+K/);可以通過原子組的方式處理:

"TTTTTTTT".match(/(?=(T+T+))2+K/);

如此便能徹底消除回溯失控問題。

五、小結

關于正則的學習,重點是要多練習多實踐,并且多嘗試用不同的方案去解決一個正則問題,一個很典型的例子,去除字符串首尾的空白,嘗試用5-10種不同的正則去測試,并思考哪些方式的效率最高,為什么?通過這一連串的思考可以帶動你學習的興趣,也會讓你成長的比較快~

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/78024.html

相關文章

  • 快速掌握正則達式 | 02 掌握進階的匹配操作

    摘要:元字符使正則表達式具有處理能力。所謂元字符就是指那些在正則表達式中具有特殊意義的專用字符,可以用來規定其前導字符即位于元字符前面的字符在目標對象中的出現模式。 掌握...

    wayneli 評論0 收藏0
  • JavaScript正則進階之路——活學妙用奇淫正則達式

    摘要:正則大法好,正則大法好,正則大法好,重要的事情說三遍。第二部分,這個部分是整個表達式的關鍵部分。學習正則如果還沒有系統學習正則表達式,這里提供一些網上經典的教程供大家學習。正則表達式使用單個字符串來描述匹配一系列匹配某個句法規則的字符串。 原文收錄在我的 GitHub博客 (https://github.com/jawil/blog) ,喜歡的可以關注最新動態,大家一起多交流學習,共同...

    BearyChat 評論0 收藏0
  • JavaScript正則進階之路——活學妙用奇淫正則達式

    摘要:正則大法好,正則大法好,正則大法好,重要的事情說三遍。第二部分,這個部分是整個表達式的關鍵部分。學習正則如果還沒有系統學習正則表達式,這里提供一些網上經典的教程供大家學習。正則表達式使用單個字符串來描述匹配一系列匹配某個句法規則的字符串。 原文收錄在我的 GitHub博客 (https://github.com/jawil/blog) ,喜歡的可以關注最新動態,大家一起多交流學習,共同...

    APICloud 評論0 收藏0
  • JavaScript正則達式進階指南

    摘要:非貪婪匹配默認情況下,正則表達式的量詞,都是進行貪婪匹配,即匹配盡可能多的字符。參考正則表達式關于專注于微信小程序微信小游戲支付寶小程序和線上應用實時監控。自從年雙十一正式上線,累計處理了億錯誤事件,付費客戶有金山軟件百姓網等眾多品牌企業。 摘要:正則表達式是程序員的必備技能,想不想多學幾招呢? showImg(https://segmentfault.com/img/bV9L9n?w...

    xietao3 評論0 收藏0

發表評論

0條評論

leo108

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<