概述
本文主要通過對正規表示式的語法進行一些簡單的介紹,從而讓沒有接觸過或者想學習正規表示式的同學有一個基礎的瞭解,從而能夠看懂和編寫使用一般的正規表示式。
本文的主要內容為:
- 正規表示式的字元匹配
- 正規表示式的位置匹配
- 正規表示式的括號與捕獲組
本文的主要受眾是想要學習正規表示式又不知道從何入手的同學。如果你已經使用過正規表示式,可以快速瀏覽本文,強化自己的記憶即可。
字元匹配
匹配規則
在正規表示式中,分為精確匹配和模糊匹配兩種。顧名思義,精確匹配就是匹配特定的字元或者位置;而非精確匹配就是帶有一定的範圍的匹配。具體示例如下:
const reg1 = /ab/; //精確匹配
const reg2 = /ab+/; //模糊匹配
複製程式碼
不同的匹配適用於不同的場景,大家根據自己的需求進行選擇即可。
字元組
在正規表示式中,我們經常會遇到從N個字元裡面選取任意字元進行匹配的需求。這個時候,我們就需要一個字元組。具體示例如下:
const reg = /[abc]/; // 與上面示例完全相同,匹配a或b或者c
複製程式碼
當匹配的字元多了以後,我們不可能全部都列到字元組裡,因此我們可以使用範圍表示法。具體示例如下:
const reg = /[a-c]/; // 匹配a或b或c
複製程式碼
相同類的字元可以用範圍,如1-9、A-Z或者a-z。在字元組中,-
是一個特殊字元,如果需要匹配-
,則需要使用\
進行轉義。
當然,如果我們是不想匹配N個字元中的任意一個,我們可以用排除字元組的方式來進行匹配。具體示例如下:
const reg = /[^abc]/; // 不匹配a、b、c中任意一個
複製程式碼
排除字元組中也可以使用範圍。
量詞
當我們需要匹配單個字元時,我們可以使用上面示例中的方法。但是,如果我們需要匹配單個字元若干次呢?最簡單的方法就是將匹配的正規表示式寫若干次,但是這樣不僅費時費力,還不方便閱讀。因此,正規表示式中使用了量詞來表示重複匹配N次的情況。
量詞含義如下:
{m, }
,至少出現m次。{m, n}
,最少出現m次,最多出現n次(最多出現N次的寫法為{0, n},而不是{, n})?
,匹配0或者1次+
,最少匹配1次,與{1, }
等價*
,匹配任意次,與{0, }
等價
瞭解了上述量詞,下面我們來看下這些量詞在示例中到底是如何應用的:
const reg1 = /a+/; //至少匹配一次a
const reg2 = /a?b*/; //匹配0或者1次a,再匹配任意次的b
複製程式碼
現在問題來了,上面示例中的/a+/
這個正規表示式,如果遇到了字串'aaa'
,那麼得到的匹配結果是什麼呢?這個就涉及到了我們下一節要介紹的內容。
貪婪匹配與非貪婪匹配
貪婪匹配:所有的量詞都會盡可能多的進行匹配,預設值。以/a+/
和'aaa'
為例,匹配的結果是'aaa'
。
非貪婪匹配:所有的兩次都會盡可能少的匹配。以/a+?/
和'aaa'
為例,匹配的結果是'a'
。
因為貪婪匹配是預設值,所以當我們寫正規表示式時,預設就是貪婪匹配。那麼我們應該如何來表示非貪婪匹配呢?具體示例如下:
const reg1 = /a+/; //貪婪匹配
const reg2 = /a+?/; // 非貪婪匹配
複製程式碼
通過上面的示例我們可以看到,我們只需要在兩次後加上一個?
,就表示是一個非貪婪匹配。
注:非貪婪匹配只會向後作用,不會向前作用。即/a+?bb/匹配'aabb'是'aabb',而不是'abb';而/aab+?/則是匹配'aab'。(這個與正規表示式匹配和回溯的原理有關,有興趣的可以閱讀我的下一篇關於正規表示式的部落格)
分支邏輯
在一個正規表示式中,我們會遇到做選擇的情況。單個元素進行選擇時,我們可以使用字元組。但是,如果需要多個元素比如ab
或者cd
進行選擇時,這個時候我們就需要分支邏輯。具體示例程式碼如下:
const reg = /ab|cd/; //表示選擇ab或者cd。為什麼不是b和c呢?這個我們在下一篇部落格——進階篇中將會講述操作符優先順序問題。
複製程式碼
位置匹配
正規表示式除了捕獲字元,還可以捕獲字串中的位置。所謂的位置,指的就是兩個字元之間。比如'ab'
這個字串,就有3個位置,分別位於a
前面、a
後面b
前面和b
後面。如果我們將位置當成是一個空字串''
,其實對於位置的匹配也可以歸納到對字元的匹配中。
匹配位置的方式也有不少,我們來看下:
^
,匹配開頭,多行模式下匹配行開頭,即每行開頭都會被匹配。$
,匹配結尾,多行模式下匹配行結尾,即每行結尾都會被匹配。\b
,\w
與\W
之間的位置(\w
表示[0-9A-Za-z],而\W
就是\w
的補集),包括開頭結尾(即也包括\w
與^
之間的位置,和\w
與$
之間的位置)\B
,與\b
相反,\w
與\w
之間的位置,和\W
與\W
之間的位置,包括開頭結尾(相對的,即包括\W
與^
之間的位置,和\W
與$
之間的位置)(?=p)
,正向肯定斷言。p
是一個子模式,匹配要在p
這個模式之前的位置(?!p)
,正向否定斷言。與(?=p)
相反,匹配不要在p
這個模式之前的位置
上面說了這麼多,下面我們通過一個示例來一下:
const reg1 = /^ab/; //對於字串'abab'來說,只會匹配到開頭的'ab'
const reg2 = /ab$/; //對於字串'abab'來說,只會匹配到結尾的'ab'
const reg3 = /\b/; //對於字串'a b'來說,會匹配到'a'前面的位置、'a'和' '之間的位置、' '和'b'之間的位置、'b'後面的位置
const reg4 = /\B/; //對於字串'aa bb[/來說,會匹配到'a'和'a'之間的位置、'b'和'b'之間的位置、'['後面的位置
const reg5 = /(?=a)/; //對於字串'bac'來說,會匹配到'a'之前的位置
const reg6 = /(?!a)/; //對於字串'bac'來說,會匹配到'b'之前的位置、'c'之前的位置以及'c'之後的位置
複製程式碼
通過上面的例子,大家應該能夠理解正規表示式在捕獲位置時候所發揮的作用。
ES2018新特性
在ES2018中,增加了反向肯定斷言
和反向否定斷言
。具體格式如下:
(?<=p)
,反向肯定斷言。p
是一個子模式,匹配要在p
模式之後的位置(?<!p)
,反向否定斷言。與(?<=p)
相反,匹配不要在p
模式之後的位置
我們通過一個具體的示例來看下:
const reg1 = /(?<=a)b/ //對於字串'abb'來說,只會匹配到'a'和'b'之間的位置。
const reg2 = /(?<=a)b/ //對於字串'abb'來說,會匹配到'b'和'b'之間的位置。
複製程式碼
括號與捕獲組
在正規表示式中,括號是一個功能非常多的操作符。本章我們將會詳細介紹正規表示式中的括號的各種作用。
提高優先順序
在正規表示式中,運算子操作也有優先順序之分,如下例所示:
const reg1 = /ab|cd/; //匹配'ab'或者'cd'
const reg2 = /a(b|c)d/;//匹配'a'後,匹配一個'b'或者'c',再匹配一個'd'
複製程式碼
關於正規表示式優先順序相關的討論,我們在此就不做展開了,有興趣的同學可以閱讀我的後一篇關於正規表示式高階進階的文章。
捕獲組與非捕獲組
如果我們在正規表示式中,我們需要獲取特定的匹配內容,那麼我們就要用到捕獲組。捕獲組通常使用(p)
,其中p
是一個子模式,表示需要捕獲的內容。具體使用示例如下:
const reg = /a(bc)d/;
let result = 'abcd'.match(reg); // 得到的result[1]就是第一個捕獲組匹配的字元'ab'
複製程式碼
但是,如果我們在一些需要保證優先順序的地方使用了小括號,但是又不想成為捕獲組來干擾匹配,我們應該怎麼辦呢?這個時候我們就需要非捕獲組。我們只需要在括號最開始加上一個?
即可。具體使用示例如下:
const reg = /a(?:bc)d/;
let result = 'abcd'.match(reg); // 得到的result沒有捕獲組
複製程式碼
反向引用
當我們在正規表示式中需要使用前面捕獲組匹配的內容時,我們可以使用反向引用。這在匹配一些成對的字元如'
和"
等時非常有效。具體使用方式如下:
const reg = /(a)b\1/; //匹配字元'aba'
複製程式碼
這裡需要注意的有三點:
- 如果出現括號巢狀的情況,那麼從左到右以第一個括號(即左開括號)的順序為準。
\10
表示的含義為第10個捕獲組,而不是第一個捕獲組加上一個字元0
。需要表示後者可以用/(\1)0/
。即使是在第三種情況下,轉移符優先順序仍然高於字元順序。- 如果在正規表示式中出現的捕獲組個數小於使用的捕獲組,那麼
\
字元就會被當成一個轉移符而非反向引用。**注:\2表示對2進行轉義的話,不同的瀏覽器對轉義後的結果是不一樣的。**下圖是Chrome瀏覽器轉義後的結果
總結
通過閱讀本文,你已經學到了正規表示式的最基礎的語法和使用規則。如果你想提高正規表示式的效率,加快正規表示式的閱讀和理解,可以閱讀正規表示式系列第二篇文章——正規表示式系列之中級進階篇。