溫馨提示:文章很長很長,保持耐心烁巫,必要時可以跳著看署隘,當然用來查也是不錯的。
正則啊亚隙,就像一座燈塔磁餐,當你在字符串的海洋不知所措的時候,總能給你一點思路阿弃;正則啊诊霹,就像一臺驗鈔機,在你不知道用戶提交的鈔票真假的時候渣淳,總能幫你一眼識別脾还;正則啊,就像一個手電筒入愧,在你需要找什么玩意的時候鄙漏,總能幫你get你要的東西...
—— 節(jié)選自 Stinson 同學的語文排比句練習《正則》
欣賞了一段文學節(jié)選后赛蔫,我們正式來梳理一遍JS中的正則,本文的首要目的是泥张,防止我經(jīng)常忘記正則的一些用法呵恢,故梳理和寫下來加強熟練度和用作參考,次要目的是與君共勉媚创,如有紕漏渗钉,請不吝賜教,良辰謝過钞钙。
本文既然取題為“一條龍”鳄橘,就要對得起”龍”,故將包括正則原理芒炼、語法一覽瘫怜、JS(ES5)中的正則、ES6對正則的擴展本刽、實踐正則的思路鲸湃,我盡量深入盡量淺出地去講這些東西(搞得好像真能深入淺出一樣的),如果你只想知道怎么應用子寓,那么看第二暗挑、三、五部分斜友,基本就能滿足你的需求了炸裆,如果想掌握JS中的正則的,那么還是委屈你跟著我的思路來吧鲜屏,嘿嘿嘿烹看!
一、原理概論
在一開始用正則的時候洛史,就覺得神奇惯殊,計算機究竟是怎么根據(jù)一個正則表達式來匹配字符串的?直到后來我遇到了一本書叫《計算理論》虹菲,看到了正則靠胜、DFA掉瞳、NFA的概念和相互間的聯(lián)系毕源,才有一些恍然小悟的意思。
但如果真的要從原理上吃透正則表達式陕习,那么恐怕最好的方式是:
- 首先去找一本專門講正則的書去看看霎褐,O’REILLY的“動物總動員”系列里就有;
- 再自己實現(xiàn)一個正則引擎该镣。
而本文的重點在于JS中正則的應用冻璃,故原理僅作簡單介紹(因為我也沒寫過正則引擎,也不深入),一來大致“糊弄下”像我一樣的好奇寶寶們對正則原理的疑惑省艳,二來知道一些原理方面基本的知識娘纷,對于理解語法和寫正則是大有裨益的。
1. 正則引擎
為什么正則能有效跋炕,因為有引擎赖晶,這和為什么JS能執(zhí)行一樣,有JS引擎辐烂,所謂正則引擎遏插,可以理解為根據(jù)你的正則表達式用算法去模擬一臺機器,這臺機器有很多狀態(tài)纠修,通過讀取待測的字符串胳嘲,在這些狀態(tài)間跳來跳去,如果最后停在了“終結狀態(tài)”(Happy Ending)扣草,那么就Say I Do了牛,否則Say You Are a Good Man。如此將一個正則表達式轉換為一個可在有限的步數(shù)中計算出結果的機器辰妙,那么就實現(xiàn)了引擎白魂。
正則的引擎大致可分為兩類:DFA和NFA
- DFA (Deterministic finite automaton) 確定型有窮自動機
- NFA (Non-deterministic finite automaton) 非確定型有窮自動機,大部分都是NFA
這里的“確定型”指上岗,對于某個確定字符的輸入福荸,這臺機器的狀態(tài)會確定地從a跳到b,“非確定型”指肴掷,對于某個確定字符的輸入敬锐,這臺機器可能有好幾種狀態(tài)的跳法;這里的“有窮”指呆瞻,狀態(tài)是有限的台夺,可以在有限的步數(shù)內確定某個字符串是被接受還是發(fā)好人卡的;這里的“自動機”痴脾,可以理解為颤介,一旦這臺機器的規(guī)則設定完成,就可以自行判斷了赞赖,不要人看滚朵。
DFA引擎不需要進行回溯,所以匹配效率一般情況下要高前域,但是它并不支持捕獲組辕近,于是也就不支持反向引用和$
這種形式的引用,也不支持環(huán)視(Lookaround)匿垄、非貪婪模式等一些NFA引擎特有的特性移宅。
如果想更詳細地了解正則归粉、DFA、NFA漏峰,那么可以去看一下《計算理論》糠悼,然后你可以根據(jù)某個正則表達式自己畫出一臺自動機。
2. 知識儲備
這一小節(jié)對于你理解正則表達式很有用浅乔,尤其是明白什么是字符绢掰,什么是位置。
2.1 正則眼中的字符串——n個字符童擎,n+1個位置
在上面的“笑聲”字符串中滴劲,一共有8個字符,這是你能看到的顾复,還有9個位置班挖,這是聰明的人才能看到的。為什么要有字符還要有位置呢芯砸?因為位置是可以被匹配的萧芙。
那么進一步我們再來理解“占有字符”和“零寬度”:
- 如果一個子正則表達式匹配到的是字符,而不是位置假丧,而且會被保存到最終的結果中双揪,那個這個子表達式就是占有字符的,比如
/ha/
(匹配ha
)就是占有字符的包帚; - 如果一個子正則匹配的是位置渔期,而不是字符,或者匹配到的內容不保存在結果中(其實也可以看做一個位置)渴邦,那么這個子表達式是零寬度的疯趟,比如
/read(?=ing)/
(匹配reading,但是只將read放入結果中谋梭,下文會詳述語法信峻,此處僅僅舉例用),其中的(?=ing)
就是零寬度的瓮床,它本質代表一個位置盹舞。
占有字符是互斥的,零寬度是非互斥的隘庄。也就是一個字符踢步,同一時間只能由一個子表達式匹配,而一個位置峭沦,卻可以同時由多個零寬度的子表達式匹配贾虽。舉個栗子,比如/aa/
是匹配不了a
的吼鱼,這個字符串中的a只能由正則的第一個a字符匹配蓬豁,而不能同時由第二個a匹配(廢話);但是位置是可以多個匹配的菇肃,比如/\b\ba/
是可以匹配a
的地粪,雖然正則表達式里有2個表示單詞開頭位置的\b
元字符,這兩個\b
是可以同時匹配位置0(在這個例子中)的琐谤。
注意:我們說字符和位置是面向字符串說的蟆技,而說占有字符和零寬度是面向正則說的。
2.2 控制權和傳動
這兩個詞可能在搜一些博文或者資料的時候會遇到斗忌,這里做一個解釋先:
控制權是指哪一個正則子表達式(可能為一個普通字符质礼、元字符或元字符序列組成)在匹配字符串,那么控制權就在哪织阳。
傳動是指正則引擎的一種機制眶蕉,傳動裝置將定位正則從字符串的哪里開始匹配。
正則表達式當開始匹配的時候唧躲,一般是由一個子表達式獲取控制權造挽,從字符串中的某一個位置開始嘗試匹配,一個子表達式開始嘗試匹配的位置弄痹,是從前一子表達匹配成功的結束位置開始的饭入。
舉一個栗子,read(?=ing)ing\sbook
匹配reading book
肛真,我們把這個正則看成5個子表達式read
谐丢、(?=ing)
、ing
蚓让、\s
庇谆、book
,當然你也可以吧read
看做4個單獨字符的子表達式凭疮,只是我們這里為了方便這么看待饭耳。read
從位置0開始匹配到位置4,后面的(?=ing)
繼續(xù)從位置4開始匹配执解,發(fā)現(xiàn)位置4后面確實是ing寞肖,于是斷言匹配成功,也就是整一個(?=ing)
就是匹配了位置4這一個位置而已(這里更能理解什么是零寬了吧)衰腌,然后后面的ing
再從位置4開始匹配到位置7新蟆,然后\s
再從位置7匹配到位置8,最后的book
從位置8匹配到位置12右蕊,整一個匹配完成琼稻。
3. 匹配之旅“淺”度游(可跳過)
說了那么多,我們把自己當做一個正則引擎饶囚,一步一步以最小的單位——“字符”和“位置”——去看一下正則匹配的過程帕翻,舉幾個栗子鸠补。
3.1 基本匹配
正則表達式:easy
源字符串:So easy
匹配過程:首先由正則表達式字符e
取得控制權,從字符串的位置0開始匹配嘀掸,遇到字符串字符‘S’紫岩,匹配失敗,然后正則引擎向前傳動睬塌,從位置1開始嘗試泉蝌,遇到字符串字符‘o’,匹配失敗揩晴,繼續(xù)傳動勋陪,后面的空格自然也失敗,于是從位置3開始嘗試匹配硫兰,成功匹配字符串字符‘e’诅愚,控制權交給正則表達式子表達式(這里也是一個字符)a
,嘗試從上次匹配成功的結束位置4開始匹配瞄崇,成功匹配字符串字符‘a’呻粹,后面一直如此匹配到‘y’,然后匹配完成苏研,匹配結果為easy
等浊。
3.2 零寬匹配
正則:^(?=[aeiou])[a-z]+$
源字符串:apple
首先這個正則表示:匹配這樣一個從頭到尾完整的字符串,這整一個字符串僅由小寫字母組成摹蘑,并且以a筹燕、e、i衅鹿、o撒踪、u這5個字母任一字母開頭。
匹配過程:首先正則的^
(表示字符串開始的位置)獲取控制權大渤,從位置0開始匹配制妄,匹配成功,控制權交給(?=[aeiou])
泵三,這個子表達式要求該位置右邊必須是元音小寫字母中的一個耕捞,零寬子表達式相互間不互斥,所以從位置0開始嘗試匹配烫幕,右側是字符串的‘a’俺抽,符合因此匹配成功,所以(?=[aeiou])
匹配此處的位置0匹配成功较曼,控制權交給[a-z]+
磷斧,從位置0開始匹配,字符串‘apple’中的每個字符都匹配成功,匹配到字符串末尾弛饭,控制權交回正則的$
冕末,嘗試匹配字符串結束位置,成功孩哑,至此栓霜,整個匹配完成翠桦。
3.3 貪婪匹配和非貪婪匹配
正則1:{.*}
正則2:{.*?}
源字符串:{233}
這里有兩個正則横蜒,在限定符(語法會講什么是限定符)后面加?
符號表示忽略優(yōu)先量詞,也就是非貪婪匹配销凑,這個栗子我剝得快一點丛晌。
首先開頭的{
匹配,兩個正則都是一樣的表現(xiàn)斗幼。
正則1的.*
為貪婪匹配疆股,所以一直匹配余下字符串'233}'拾酝,匹配到字符串結束位置,只是每次匹配,都記錄一個備選狀態(tài)沼死,為了以后回溯,每次匹配有兩條路拓哟,選擇了匹配這條路主到,但記一下這里還可以有不匹配這條路,如果前面死胡同了阴挣,可以退回來气堕,此時控制權交還給正則的}
,去匹配字符串結束位置畔咧,失敗茎芭,于是回溯,意思就是說前面的.*
你吃的太多了誓沸,吐一個出來梅桩,于是控制權回給.*
,吐出一個}
(其實是用了前面記錄的備選狀態(tài)拜隧,嘗試不用.*
去匹配'}')宿百,控制權再給正則的}
,這次匹配就成功了虹蓄。
正則2的.*?
為非貪婪匹配犀呼,盡可能少地匹配,所以匹配'233}'的每一個字符的時候薇组,都是嘗試不匹配外臂,但是一但控制權交還給最后的}
就發(fā)現(xiàn)出問題了,趕緊回溯乖乖匹配律胀,于是每一個字符都如此宋光,最終匹配成功貌矿。
云里霧里?這就對了罪佳!可以移步去下面推薦的博客看看:
想詳細了解貪婪和非貪婪匹配原理以及獲取更多正則相關原理逛漫,除了看書之外,推薦去一個CSDN的博客 雁過無痕-博客頻道 - CSDN.NET 赘艳,講解得很詳細和透徹
二酌毡、語法一覽
正則的語法相信許多人已經(jīng)看過deerchao寫的30分鐘入門教程,我也是從那篇文字中入門的蕾管,deerchao從語法邏輯的角度以.NET正則的標準來講述了正則語法枷踏,而我想重新組織一遍,以便于應用的角度掰曾、以JS為宿主語言來重新梳理一遍語法旭蠕,這將便于我們把語言描述翻譯成正則表達式。
下面這張一覽圖(可能需要放大)旷坦,整理了常用的正則語法掏熬,并且將JS不支持的語法特性以紅色標注出來了(正文將不會描述這些不支持的特性),語法部分的詳細描述也將根據(jù)下面的圖秒梅,從上到下旗芬,從左到右的順序來梳理,盡量不啰嗦番电。
1. 要用某類常見字符——簡單元字符
為什么這里要加簡單2個字岗屏,因為在正則中,\d
漱办、\w
這樣的叫元字符这刷,而{n,m}
、(?!exp)
這樣的也叫元字符娩井,所以元字符是在正則中有特定意義的標識暇屋,而這一小節(jié)講的是簡單的一些元字符。
-
.
匹配除了換行符以外的任意字符洞辣,也即是[^\n]
咐刨,如果要包含任意字符,可使用(.|\n)
-
\w
匹配任意字母扬霜、數(shù)字或者下劃線定鸟,等價于[a-zA-Z0-9_]
,在deerchao的文中還指出可匹配漢字著瓶,但是\w在JS中是不能匹配漢字的 -
\s
匹配任意空白符联予,包含換頁符\f
、換行符\n
、回車符\r
沸久、水平制表符\t
季眷、垂直制表符\v
-
\d
匹配數(shù)字 -
\un
匹配n,這里的n是一個有4個十六進制數(shù)字表示的Unicode字符卷胯,比如\u597d
表示中文字符“好”子刮,那么超過\uffff
編號的字符怎么表示呢?ES6的u修飾符會幫你窑睁。
2. 要表示出現(xiàn)次數(shù)(重復)——限定符
-
a*
表示字符a連續(xù)出現(xiàn)次數(shù) >= 0 次 -
a+
表示字符a連續(xù)出現(xiàn)次數(shù) >= 1 次 -
a?
表示字符a出現(xiàn)次數(shù) 0 或 1 次 -
a{5}
表示字符a連續(xù)出現(xiàn)次數(shù) 5 次 -
a{5,}
表示字符a連續(xù)出現(xiàn)次數(shù) >= 5次 -
a{5,10}
表示字符a連續(xù)出現(xiàn)次數(shù)為 5到10次 挺峡,包括5和10
3. 匹配位置——定位符和零寬斷言
匹配某個位置的表達式都是零寬的,這是主要包含兩部分卵慰,一是定位符沙郭,匹配一個特定位置佛呻,二是零寬斷言裳朋,匹配一個要滿足某要求的位置。
定位符有以下幾個常用的:
-
\b
匹配單詞邊界位置吓著,準確的描述是它匹配一個位置鲤嫡,這個位置前后不全是\w
能描述的字符,所以像\u597d\babc
是可以匹配“好abc”的绑莺。 -
^
匹配字符串開始位置暖眼,也就是位置0,如果設置了 RegExp 對象的 Multiline 屬性纺裁,^
也匹配 '\n' 或 '\r' 之后的位置 -
$
匹配字符串結束位置诫肠,如果設置了RegExp 對象的 Multiline 屬性,$
也匹配 '\n' 或 '\r' 之前的位置
零寬斷言(JS支持的)有以下兩個:
-
(?=exp)
匹配一個位置欺缘,這個位置的右邊能匹配表達式exp栋豫,注意這個表達式僅僅匹配一個位置,只是它對于這個位置的右邊有要求谚殊,而右邊的東西是不會被放進結果的丧鸯,比如用read(?=ing)
去匹配“reading”,結果是“read”嫩絮,而“ing”是不會放進結果的 -
(?!exp)
匹配一個位置丛肢,這個位置的右邊不能匹配表達式exp
4. 想表達“或”的意思——字符簇和分歧
我們經(jīng)常會表達“或”的含義,比如這幾個字符中的任意一個都行剿干,再比如匹配5個數(shù)字或者5個字母都行等等需求蜂怎。
字符簇可用來表達字符級別的“或”語義,表示的是方括號中的字符任選一:
-
[abc]
表示a置尔、b杠步、c這3個字符中的任意一個,如果字母或者數(shù)字是連續(xù)的,那么可以用-
連起來表示篮愉,[b-f]
代表從b到f這么多字符中任選一個 -
[(ab)(cd)]
并不會用來匹配字符串“ab”或“cd”腐芍,而是匹配a、b试躏、c猪勇、d、(颠蕴、)這6個字符中的任一個泣刹,也就是想表達“匹配字符串ab或者cd”這樣的需求不能這么做,要這么寫ab|cd
犀被。但這里要匹配圓括號本身椅您,講道理是要反斜杠轉義的,但是在方括號中寡键,圓括號被當成普通字符看待掀泳,即便如此,仍然建議顯式地轉義
分歧用來表達表達式級別的“或”語義西轩,表示的是匹配|
左右任一表達就可:
-
ab|cd
會匹配字符串“ab”或者“cd” -
會短路员舵,回想下編程語言中邏輯或的短路,所以用
(ab|abc)
去匹配字符串“abc”藕畔,結果會是“ab”马僻,因為豎線左邊的已經(jīng)滿足了,就用左邊的匹配結果代表整個正則的結果
5. 想表達“非”的意思——反義
有時候我們想表達“除了某些字符之外”這樣的需求注服,這個時候就要用到反義
-
\W
韭邓、\D
、\S
溶弟、\B
用大寫字母的這幾個元字符表示就是對應小寫字母匹配內容的反義女淑,這幾個依次匹配“除了字母、數(shù)字可很、下劃線外的字符”诗力、“非數(shù)字字符”、“非空白符”我抠、“非單詞邊界位置” -
[^aeiou]
表示除了a苇本、e、i菜拓、o瓣窄、u外的任一字符,在方括號中且出現(xiàn)在開頭位置的^
表示排除纳鼎,如果^
在方括號中不出現(xiàn)在開頭位置俺夕,那么它僅僅代表^
字符本身
6. 整體看待和捕獲——分組和后向引用
其實你在上面的一些地方已經(jīng)看到了圓括號裳凸,是的,圓括號就是用來分組的劝贸,括在一對括號里的就是一個分組姨谷。
上面講的大部分是針對字符級別的,比如重復字母 “A” 5次映九,可以用A{5}
來表示梦湘,但是如果想要字符串“ABC”重復5次呢?這個時候就需要用到括號件甥。
括號的第一個作用捌议,將括起來的分組當做一個整體看待,所以你可以像對待字符重復一樣在一個分組后面加限定符引有,比如(ABC){5}
瓣颅。
分組匹配到的內容也就是這個分組捕獲到的內容,從左往右譬正,以左括號為標志宫补,每個分組會自動擁有一個從1開始的編號,編號0的分組對應整個正則表達式导帝,JS不支持捕獲組顯示命名守谓。
括號的第二個作用,分組捕獲到的內容您单,可以在之后通過\分組編號
的形式進行后向引用。比如(ab|cd)123\1
可以匹配“ab123ab”或者“cd123cd”荞雏,但是不能匹配“ab123cd”或“cd123ab”虐秦,這里有一對括號,也是第一對括號凤优,所以編號為捕獲組1悦陋,然后在正則中通過\1
去引用了捕獲組1的捕獲的內容,這叫后向引用筑辨。
括號的第三個作用俺驶,改變優(yōu)先級,比如abc|de
和(abc|d)e
表達的完全不是一個意思棍辕。
7. 轉義
任何在正則表達式中有作用的字符都建議轉義暮现,哪怕有些情況下不轉義也能正確,比如[]
中的圓括號楚昭、^
符號等栖袋。
8. 優(yōu)先級問題
優(yōu)先級從高到低是:
- 轉義 \
- 括號(圓括號和方括號)
(), (?:), (?=), []
- 字符和位置
- 豎線
|
9. 貪婪和非貪婪
在限定符中,除了{n}
確切表示重復幾次抚太,其余的都是一個有下限的范圍塘幅。
在默認的模式(貪婪)下昔案,會盡可能多的匹配內容。比如用ab*
去匹配字符串“abbb”电媳,結果是“abbb”踏揣。
而通過在限定符后面加問號?
可以進行非貪婪匹配,會盡可能少地匹配匾乓。用ab*?
去匹配“abbb”呼伸,結果會是“a”。
不帶問號的限定符也稱匹配優(yōu)先量詞钝尸,帶問號的限定符也稱忽略匹配優(yōu)先量詞括享。
10. 修飾符(匹配選項)
其實正則的匹配選項有很多可選,不同的宿主語言環(huán)境下可能各有不同珍促,此處就JS的修飾符作一個說明:
- 加g修飾符:表示全局匹配铃辖,模式將被應用到所有字符串,而不是在發(fā)現(xiàn)第一個匹配項時停止
- 加i修飾符:表示不區(qū)分大小寫
- 加m修飾符:表示多行模式猪叙,會改變
^
和$
的行為娇斩,上文已述
三、JS(ES5)中的正則
JS中的正則由引用類型RegExp表示穴翩,下面主要就RegExp類型的創(chuàng)建犬第、兩個主要方法和構造函數(shù)屬性來展開,然后會提及String類型上的模式匹配芒帕,最后會簡單羅列JS中正則的一些局限歉嗓。
1. 創(chuàng)建正則表達式
一種是用字面量的方式創(chuàng)建,一種是用構造函數(shù)創(chuàng)建背蟆,我們始終建議用前者鉴分。
//創(chuàng)建一個正則表達式
var exp = /pattern/flags;
//比如
var pattern=/\b[aeiou][a-z]+\b/gi;
//等價下面的構造函數(shù)創(chuàng)建
var pattern=new RegExp("\\b[aeiou][a-z]+\\b","gi");
其中pattern可以是任意的正則表達式,flags部分是修飾符带膀,在上文中已經(jīng)闡述過了志珍,有 g、i垛叨、m 這3個(ES5中)伦糯。
現(xiàn)在說一下為什么不要用構造函數(shù),因為用構造函數(shù)創(chuàng)建正則嗽元,可能會導致對一些字符的雙重轉義敛纲,在上面的例子中,構造函數(shù)中第一個參數(shù)必須傳入字符串(ES6可以傳字面量)还棱,所以字符
會被轉義成\载慈,因此字面量的\b
會變成字符串中的\\\b
,這樣很容易出錯珍手,賊多的反斜杠办铡。
2. RegExp上用來匹配提取的方法——exec()
var matches=pattern.exec(str);
接受一個參數(shù):源字符串
返回:結果數(shù)組辞做,在沒有匹配項的情況下返回null
結果數(shù)組包含兩個額外屬性,index表示匹配項在字符串中的位置寡具,input表示源字符串秤茅,結果數(shù)組matches第一項即matches[0]
表示匹配整個正則表達式匹配的字符串,matches[n]
表示于模式中第n個捕獲組匹配的字符串童叠。
要注意的是框喳,第一,exec()永遠只返回一個匹配項(指匹配整個正則的)厦坛,第二五垮,如果設置了g
修飾符,每次調用exec()會在字符串中繼續(xù)查找新匹配項杜秸,不設置g
修飾符放仗,對一個字符串每次調用exec()永遠只返回第一個匹配項。所以如果要匹配一個字符串中的所有需要匹配的地方撬碟,那么可以設置g
修飾符诞挨,然后通過循環(huán)不斷調用exec方法。
//匹配所有ing結尾的單詞
var str="Reading and Writing";
var pattern=/\b([a-zA-Z]+)ing\b/g;
var matches;
while(matches=pattern.exec(str)){
console.log(matches.index +' '+ matches[0] + ' ' + matches[1]);
}
//循環(huán)2次輸出
//0 Reading Read
//12 Writing Writ
3. RegExp上用來測試匹配成功與否的方法——test()
var result=pattern.test(str);
接受一個參數(shù):源字符串
返回:找到匹配項呢蛤,返回true惶傻,沒找到返回false
4. RegExp構造函數(shù)屬性
RegExp構造函數(shù)包含一些屬性,適用于作用域中的所有正則表達式其障,并且基于所執(zhí)行的最近一次正則表達式操作而變化银室。
-
RegExp.input
或RegExp["$_"]
:最近一次要匹配的字符串 -
RegExp.lastMatch
或RegExp["$&"]
:最近一次匹配項 -
RegExp.lastParen
或RegExp["$+"]
:最近一次匹配的捕獲組 -
RegExp.leftContext
或RegExp["$`"]
:input字符串中l(wèi)astMatch之前的文本 -
RegExp.rightContext
或RegExp["$'"]
:input字符串中l(wèi)astMatch之后的文本 -
RegExp["$n"]
:表示第n個捕獲組的內容,n取1-9
5. String類型上的模式匹配方法
上面提到的exec和test都是在RegExp實例上的方法静秆,調用主體是一個正則表達式粮揉,而以字符串為主體調用模式匹配也是最為常用的。
5.1 匹配捕獲的match方法
在字符串上調用match方法抚笔,本質上和在正則上調用exec相同,但是match方法返回的結果數(shù)組是沒有input和index屬性的侨拦。
var str="Reading and Writing";
var pattern=/\b([a-zA-Z]+)ing\b/g;
//在String上調用match
var matches=str.match(pattern);
//等價于在RegExp上調用exec
var matches=pattern.exec(str);
5.2 返回索引的search方法
接受的參數(shù)和match方法相同殊橙,要么是一個正則表達式,要么是一個RegExp對象狱从。
//下面兩個控制臺輸出是一樣的膨蛮,都是5
var str="I am reading.";
var pattern=/\b([a-zA-Z]+)ing\b/g;
var matches=pattern.exec(str);
console.log(matches.index);
var pos=str.search(pattern);
console.log(pos);
5.3 查找并替換的replace方法
var result=str.replace(RegExp or String, String or Function);
第一個參數(shù)(查找):RegExp對象或者是一個字符串(這個字符串就被看做一個平凡的字符串)
第二個參數(shù)(替換內容):一個字符串或者是一個函數(shù)
返回:替換后的結果字符串,不會改變原來的字符串
第一個參數(shù)是字符串
只會替換第一個子字符串
第一個參數(shù)是正則
指定g
修飾符季研,則會替換所有匹配正則的地方敞葛,否則只替換第一處
第二個參數(shù)是字符串
可以使用一些特殊的字符序列,將正則表達式操作的值插進入与涡,這是很常用的惹谐。
-
$n
:匹配第n個捕獲組的內容持偏,n取0-9 -
$nn
:匹配第nn個捕獲組內容,nn取01-99 -
$`
:匹配子字符串之后的字符串 -
$'
:匹配子字符串之前的字符串 -
$&
:匹配整個模式得字符串 -
$$
:表示$
符號本身
第二個參數(shù)是一個函數(shù)
- 在只有一個匹配項的情況下氨肌,會傳遞3個參數(shù)給這個函數(shù):模式的匹配項鸿秆、匹配項在字符串中的位置、原始字符串
- 在有多個捕獲組的情況下怎囚,傳遞的參數(shù)是模式匹配項卿叽、第一個捕獲組、第二個恳守、第三個...最后兩個參數(shù)是模式的匹配項在字符串位置考婴、原始字符串
這個函數(shù)要返回一個字符串,表示要替換掉的匹配項
5.4 分隔字符串的split
基于指定的分隔符將一個字符串分割成多個子字符串催烘,將結果放入一個數(shù)組沥阱,接受的第一個參數(shù)可以是RegExp對象或者是一個字符串(不會被轉為正則),第二個參數(shù)可選指定數(shù)組大小颗圣,確保數(shù)組不會超過既定大小喳钟。
6 JS(ES5)中正則的局限
JS(ES5)中不支持以下正則特性(在一覽圖中也可以看到):
- 匹配字符串開始和結尾的\A和\Z錨
- 向后查找(所以不支持零寬度后發(fā)斷言)
- 并集和交集類
- 原子組
- Unicode支持(\uFFFF之后的)
- 命名的捕獲組
- 單行和無間隔模式
- 條件匹配
- 注釋
四、ES6對正則的主要加強
ES6對正則做了一些加強在岂,這邊僅僅簡單羅列以下主要的3點奔则,具體可以去看ES6
1. 構造函數(shù)可以傳正則字面量了
ES5中構造函數(shù)是不能接受字面量的正則的,所以會有雙重轉義蔽午,但是ES6是支持的易茬,即便如此,還是建議用字面量創(chuàng)建及老,簡潔高效抽莱。
2. u修飾符
加了u
修飾符,會正確處理大于\uFFFF
的Unicode骄恶,意味著4個字節(jié)的Unicode字符也可以被支持了食铐。
// \uD83D\uDC2A是一個4字節(jié)的UTF-16編碼,代表一個字符
/^\uD83D/u.test('\uD83D\uDC2A')
// false僧鲁,加了u可以正確處理
/^\uD83D/.test('\uD83D\uDC2A')
// true虐呻,不加u,當做兩個unicode字符處理
加了u
修飾符寞秃,會改變一些正則的行為:
-
.
原本只能匹配不大于\uFFFF
的字符斟叼,加了u
修飾符可以匹配任何Unicode字符 - Unicode字符新表示法
\u{碼點}
必須在加了u
修飾符后才是有效的 - 使用
u
修飾符后,所有量詞都會正確識別碼點大于0xFFFF
的Unicode字符 - 使一些反義元字符對于大于
\uFFFF
的字符也生效
3. y修飾符
y修飾符的作用與g修飾符類似春寿,也是全局匹配朗涩,開始從位置0開始,后一次匹配都從上一次匹配成功的下一個位置開始绑改。
不同之處在于谢床,g修飾符只要剩余位置中存在匹配就可兄一,而y修飾符確保匹配必須從剩余的第一個位置開始。
所以/a/y
去匹配"ba"
會匹配失敗萤悴,因為y修飾符要求瘾腰,在剩余位置第一個位置(這里是位置0)開始就要匹配。
ES6對正則的加強覆履,可以看這篇
五蹋盆、應用正則的實踐思路
應用正則,一般是要先想到正則(廢話)硝全,只要看到和“找”相關的需求并且這個源是可以被字符串化的栖雾,就可以想到用正則試試。
一般在應用正則有兩類情況伟众,一是驗證類問題析藕,另一類是搜索、提取凳厢、替換類問題账胧。驗證,最常見的如表單驗證先紫;搜索治泥,以某些設定的命令加關鍵詞去搜索;提取遮精,從某段文字中提取什么居夹,或者從某個JSON對象中提取什么(因為JSON對象可以字符串化啊)本冲;替換准脂,模板引擎中用到。
1. 驗證類問題
驗證類問題是我們最常遇到的檬洞,這個時候其實源字符串長什么樣我們是不知道狸膏,鬼知道萌萌噠的用戶會做出什么邪惡的事情來,推薦的方式是這樣的:
- 首先用白話描述清楚你要怎樣的字符串添怔,描述好了之后环戈,就開腦洞地想用戶可能輸入什么奇怪的東西,就是自己舉例澎灸,拿一張紙可舉一大堆的,有接受的和不接受的(這個是你知道的)遮晚,這個過程中可能你會去修改之前的描述性昭;
- 把你的描述拆解開來,翻譯成正則表達式县遣;
- 測試你的正則表達式對你之前舉的例子的判斷是不是和你預期一致糜颠,這里就推薦用在線的JS正則測試去做汹族,不要自己去一遍遍寫了。
2. 搜索其兴、提取顶瞒、替換類問題
這類問題,一般我們是知道源文本的格式或者大致內容的元旬,所以在解決這類問題時一般已經(jīng)會有一些測試的源數(shù)據(jù)榴徐,我們要從這些源數(shù)據(jù)中提取出什么、或者替換什么匀归。
- 找到這些手上的源數(shù)據(jù)中你需要的部分坑资;
- 觀察這些部分的特征,這些部分本身的特征以及這些部分周圍的特征穆端,比如這部分前一個符號一定是一個逗號袱贮,后一個符號一定是一個冒號,總之就是找規(guī)律体啰;
- 考察你找的特征攒巍,首先能不能確切地標識出你要的部分,不會少也不會多荒勇,然后考慮下以后的源數(shù)據(jù)也是如此么柒莉,以后會不會這些特征就沒有了;
- 組織你對要找的這部分的描述枕屉,描述清楚經(jīng)過你考察的特征常柄;
- 翻譯成正則表達式;
- 測試搀擂。
終于絮絮叨叨寫完了西潘,1萬多字有關JS正則的講解,寫完發(fā)現(xiàn)自己對正則的熟練又進了一步哨颂,所以推薦大家經(jīng)常做做梳理喷市,很有用,然后樂于分享威恼,于己于人都是大有裨益品姓,感謝能看完的所有人。
我沒有仔細地審稿箫措,大家遇到什么問題腹备,或者發(fā)現(xiàn)有什么紕漏之處,還望大家指出斤蔓,留言就好植酥。我會及時修改。