工作中,正則表達式用的可能不是很多,一般使用的時候網上都有現成的實例,很少缺乏比較全面的理解。本文主要以匹配HTML標簽為例,簡述下正則表達式常用的功能點。匹配HTML片段如下:
let str = ` <div id="app"> <div>21</div> <h1> <span>hello</span> <span>smile</span> </h1> <button>按鈕</button></div> `;
正則匹配本文查看結果主要用String.match(RegExp)方法,常用的正則匹配方法為:RegExp.test(String),RegExp.exec(String)
1、正則表達式的聲明
正則表達式的聲明方式和普通變量的聲明方式類似,一般情況下有兩種,字面量和正則構造方法RegExp。以匹配span標簽為例,匹配單個span標簽的正則如下:
// 字面量方式 let normalPattern = /span/ // 調用正則構造方法 let pattern = new RegExp('span')
2. 修飾符
上述的正則,在匹配測試文本是,輸出的結果是一個數組,只有一個元素span,如果我想匹配多個呢,這里就要引入正則修飾符的概念了,常用的就是 i 和 g。
i的含義是忽略大小寫,比如用/span/只能去匹配span字符串,SPAN匹配不了,如果想匹配SPAN,就要加上i修飾符
g的含義是global,全局查找,正則默認只匹配第一個符合條件的值,如果要全局匹配,就要加上g修飾符。
具體寫法如下:
// 字面量方式 let normalPattern = /span/ig // 調用正則構造方法 let pattern = new RegExp('span', 'ig')
3.字符類別
以上匹配的文本都非常的基礎,假如我想匹配所有的HTML標簽,該怎么寫呢?HTML標簽很多,沒法一個個全部羅列出來,我們可以使用字符類別,進行快速的匹配。參考列表如下:
字符類別,在某種程度上來說,可以理解為一個字符集,如上述列表所示,\d是數字0-9的集合,如果我們要匹配一段文本中所有的數字,可以直接聲明如下正則:
let numberPattern = /\d/g
假如我要匹配所有的HTML標簽呢?在匹配一個文本之前,我們需要先分析下HTML標簽規律,首先,HTML標簽和一般的XML標簽,都是以尖括號<大頭,以尖括號>結尾,其次,中間只能是英文字母或者數字,最后,數字或字母可能出現一次或者多次。寫正則的第一要素是先歸納總結,總結出規律才能去分析正則的寫法。正則表達式本質上也是一種規律的解釋文本。從我們總結的規律中,我們可以按照如下寫法:
let matchTags = /<\w+>/g
匹配結果如下:
[ '<div>', '<h1>', '<span>', '<span>', '<button>' ]
\w代表數字或者字母,+代表匹配一次或者多次,g代表全局匹配。我們在匹配文本的時候,一種規律有時候不可能只用一次,正則表達式提供了豐富的數量詞語法,能夠滿足我們絕大多數要求,下面的章節會有介紹。
我們看到,我們匹配的HTML標簽只是前面的標簽,並沒有后面的閉合標簽,我們如何匹配閉合標簽呢?閉合標簽比普通的標簽多了一個斜杠/,如果我們單純的寫/,是不可以的,大家可能意識到,正則表達式的字面量寫法是兩個斜杠/包起來的,如果我們直接寫,會有沖突,這時候,我們需要借助轉義字符反斜杠\進行轉義即可,一些與正則表達式沖突的,都需要用轉義字符進行轉義,比如特殊字符(),[],還有是一些具有特殊意義的集合,\w,\d....
匹配閉合標簽的正則如下:
let matchCloseTags = /<\/\w+>/g
匹配結果:
[ '</div>', '</span>', '</span>', '</h1>', '</button>', '</div>' ]
4.數量詞
在字符類中,我們用到了數量詞+,代表匹配一次或多次。正則表達式數量詞遠遠不止這些,具體列表如下所示:
舉個例子,假如我要匹配HTML片段中的h標簽,我們都知道h標簽的基本規則為只有一個字母和數字,從上述我們可以得出,我們可以用數量詞{n},正則表達式如下:
let matchH = /<h{1}\d{1}>/g
匹配結果:
[ '<h1>' ]
在這里,我們要記住兩個個數量詞,x*?和x+?,這個是最小匹配原則。假如現在我們要匹配第一個span閉合標簽和文本,即<span>hello</span>,如果我們按照正常思路去寫,以<span>開頭,</span>結尾,中間是任意字符,正則表達式實現如下所示:
let matchSpan = /<span>(\S|\s)+<\/span>/g
匹配結果可能和我們預想的不大一樣,實際上匹配結果只有一個,如下所示:
[ '<span>hello</span>\n <span>smile</span>' ]
正則默認的會全文匹配,並不會按照最小匹配原則,即找到符合條件的文本就返回,他會最大程度的去匹配符合條件的文本。如果我們要分別匹配兩段span標簽,我們就需要用到最小匹配原則的數量詞,具體如下:
let matchSpan = /<span>(\S|\s)*?<\/span>/g
let matchSpan2 = /<span>(\S|\s)+?<\/span>/g
文本匹配結果:
[ '<span>hello</span>', '<span>smile</span>' ]
5.集合
正則表達式中,集合使用也比較多,語法比較簡單,所有需要匹配的文本依次放在中括號[]中,比如如果你想匹配abc三個字母,pattern=/[abc]/,如果不想匹配abc,那就是pattern=[^abc],如果匹配數字,那就是/[0-9]/,-代表一個連續的集合,字母就是[a-zA-Z]
6.邊界
邊界最簡單的用法,^代表匹配輸入的開始,$代表匹配輸入的結束。
7.分組
假如現在有這樣一個需求,需要匹配span標簽里的文本,而不需要span標簽, 此時可能會用到分組,從字面意思上來說,分組就是把規則分類。分組的語法比較簡單,就是小括號()。轉換為正則的語法如下:
let matchSpanText = /(<span>)(\S+)+?(<\/span>)/
let result = str.match(matchSpanText)
匹配結果如下圖所示:
從上圖可以看出,匹配結果數組result長度為4,第一部為匹配的結果,第二部分為(span)匹配的結果,第三部分為(\S+)匹配的結果,第四部分為(</span>)匹配的結果,如果我們要拿純文本,直接去arr[2]即可。我們也能得出一個結論,所謂分組,其實就是普通的正則匹配加上分組條件的匹配集合。
8.斷言
斷言是個好東西,能夠解決許多比較棘手的問題,比如上面的例子,我想拿兩個標簽中間的文本,采用分組去取,是可以實現,但是就看每次都要拿匹配結果的第三個數組,有點不爽,怎么辦呢,斷言可以幫你解決這個煩惱,匹配的結果直接是文本。斷言的基本規則如下:
從基本規則中,我們可以從字面上得出,斷言可以設置一個文本前后的匹配規則,我們可以將匹配標簽中間的文本規則改造如下:
let matchSpanTextDescribe = /(?<=<span>)(\S+)+?(?=<\/span>)/g
let result = str.match(matchSpanTextDescribe)
匹配結果如下:
參考資料:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp#boundaries