正則表達式是匹配模式,要麼匹配字符,要麼匹配位置(請務必記住這句話)javascript
若是正則只有精確匹配是沒有多少意義的,好比/hello/
,也只能匹配字符串中的"hello" 這個子串。html
var regex = /hello/;
console.log(regex.test("hello"));
// => true
複製代碼
正則表達式之因此強大,是由於其可以實現模糊匹配。java
而模糊匹配,有兩個方向上的模糊:橫向模糊和縱向模糊git
橫向模糊指的是,一個正則可匹配的字符串的長度是不固定的,能夠是多種狀況的,長度在宏觀角度可不就是橫向的嗎?正則表達式
其實現的方式是使用量詞、好比{m,n}
,表示連續出現 m 次,最多 n 次。express
好比正則 /ab{2,5}c/
表示匹配這樣一個字符串,第一個字符是"a",接下來是 2到5個字符 "b",最後是字符 "c"。bash
其可視化的形式以下:ide
測試以下:測試
var regex = /ab{2,5}c/g;
var string = "abc abbc abbbc abbbbc abbbbbc abbbbbbc"
console.log(string.match(regex));
// => ["abbc","abbbc","abbbbc","abbbbbc"]
複製代碼
案例中的用的是
/ab{2,5}c/g
其中g 是正則的一個修飾符,表示全局匹配,即,在目標字符串中按照順序找到知足匹配模式的全部字串,強調是「全部」,而不是「第一個」。g 是單詞global 的首字母優化
縱向模糊匹配指的是,一個正則匹配的字符串,具體到某一位字符的時候,它能夠不是某個肯定的字符,能夠有多種可能。
其實現方式是使用字符組。好比[abc],表示該字符能夠是 "a","b","c"中的任何一個。
好比:/a[123]b/
能夠匹配以下三種字符串 "a1b","a2b","a3b".
其可視化形式以下:
測試以下:
var regex = /a[123]b/g;
var string = "a0b a1b a2b a3b a4b";
console.log(string.match(regex));
// => ["a1b", "a2b", "a3b"]
複製代碼
以上就是介紹的兩種模糊匹配模式,只要掌握了橫向和縱向的模糊匹配,就可以解決大部分的正則匹配問題。
須要強調的是,雖然字符組(字符類),但只是其中的一個字符。 例如[abc]表示匹配一個字符,他能夠是 「a」、「b「、」c「 之一。
若是字符組裏面的字符特別多的話,怎麼辦?可使用範圍表示法。
好比 [123456abcdefGHIJKLM] 能夠寫成 [1-6a-fG-M]。用連字符 - 來省略和簡寫。
由於連字符有特殊的用途,那麼要匹配 "a"、"-"、"z" 這三者中任意一個字符,怎麼作呢?
不能寫成 [a-z],由於這種方式表示小寫字符中的任意一個字符。
能夠寫成以下方式:[-az] 或者 [az-] 或者 [a-z].
即要麼放在開頭,要麼放在結尾,要麼轉義,總之不會讓引擎認爲是範圍表示法就好了。
在縱向模糊匹配中,還有一種情形就是,某一個字符能夠是任何東西,但就是不能是"a"、"b"、"c".
這個時候就是排除字符組(反義字符組)的概念,例如[^abc].表示是一個除"a"、"b"、"c" 以外的任意一個字符,字符組的第一位放置 ^ (脫字符),表示求反的概念。
有了字符組的概念後,一些常見的符號咱們也就理解了。由於它們都是系統自帶的簡寫形式。
字符組 | 具體含義 |
---|---|
\d | 表示 [0-9]。表示是一位數字。 記憶方式:其英文是 digit(數字)。 |
\D | 表示 [^0-9]。表示除數字外的任意字符。 |
\w | 表示 [0-9a-zA-Z_]。表示數字、大小寫字母和下劃線。 記憶方式:w 是 word 的簡寫,也稱單詞字符。 |
\W | 表示 [^0-9a-zA-Z_]。非單詞字符。 |
\s | 表示 [ \t\v\n\r\f]。表示空白符,包括空格、水平製表符、垂直製表符、換行符、回車符、換頁 符。記憶方式:s 是 space 的首字母,空白符的單詞是 white space。 |
\S | 表示 [^ \t\v\n\r\f]。 非空白符。 |
. | 表示 [^\n\r\u2028\u2029]。通配符,表示幾乎任意字符。換行符、回車符、行分隔符和段分隔符 除外。記憶方式:想一想省略號 ... 中的每一個點,均可以理解成佔位符,表示任何相似的東西。 |
若是要匹配任意字符怎麼辦?可使用[\d\D]、[\w\W]、[\s\S] 和 [^] 中任何的一個。
以上各字符組對應的可視化形式是:
量詞也稱之爲重複,掌握{m,n}的準確含義以後,只須要記住一些簡寫形式。
字符組 | 具體含義 |
---|---|
{m,} | 表示至少出現m次 |
{m} | 等價於 {m,m},表示出現 m 次。 |
? | 等價於 {0,1},表示出現或者不出現。 記憶方式:問號的意思表示,有嗎? |
+ | 等價於 {1,},表示出現至少一次。 記憶方式:加號是追加的意思,得先有一個,而後才考慮追加。 |
* | 等價於 {0,},表示出現任意次,有可能不出現。 記憶方式:看看天上的星星,可能一顆沒有,可能 零散有幾顆,可能數也數不過來 |
以上量詞 對應的可視的形式是:
看下面的例子:
var regex = /\d{2,5}/g;
var string = "123 1234 12345 123456";
console.log(string.match(regex));
// => ["123", "1234", "12345", "12345"]
複製代碼
其中正則 /\d{2,5}/ 表示數字連續出現 2 到 5 次,會匹配 2 位 3 位 4 位 5 位連續的數字。
這種形式的【匹配模式實際上是貪婪的,它會盡量多的匹配 你給我 6 給我就要 5 個 你給我 3 個我就要 3 個。反正 在力所能及的範圍內,越多越好。
咱們知道有時候貪婪並非一件好事,而惰性匹配就是儘量少的 匹配。
var regex = /\d{2,5}?/g;
var string = "123 1234 12345 123456";
console.log(string.match(regex));
// => ["12", "12", "34", "12", "34", "12", "34", "56"]
複製代碼
其中 /\d{2,5}?/ 表示 雖然2到5次都行 當2個就夠的時候,就再也不往下嘗試了。
一個模式能夠實現橫向和縱向的模糊匹配。而多選分支能夠支持多個子模式任選其一: 具體的形式以下:(p1|p2|p3) 其中p1,p2,p3 是子模式,用 |
(管道符) 分割,表示其中任何之一。
例如要匹配字符串"good"和"nice" 可使用/good|nice/。
可視化形式以下:
測試以下:
var regex = /good|nice/g;
var string = "good idea,nice try.";
console.log( string match((regex)) );
// => ["good","nice"]
複製代碼
可是有個事實咱們應該注意,好比咱們用/good|goodbye/、去匹配 "goodbye"字符串時候 結果是 "good"
var regex = /good|goodbye/g;
var string = "goodbye";
console.log( string.match(regex) );
// => ['good']
複製代碼
若是將正則改爲/goodbye|good/,結果是:
var regex = /goodbye|good/g;
var string = "goodbye";
console.log( string.match(regex) );
// => ['goodbye']
複製代碼
也就是說,分支結構也是惰性的,即當前的模式匹配上了,後面的就不會再嘗試了。
匹配字符,無非就是字符組,量詞和分支結構的組合使用罷了。 下面經過幾個例子演示一下使用方式:
要求匹配
##ffbbad
##Fc01DF
##FFF
##ffe
複製代碼
分析:
表示一個16進制,能夠用字符組[0-9a-fA-F]
; 其中字符組能夠出現 3或者6次,須要使用量詞和分支結構。
使用分支結構的時候須要注意順序。
正則以下:
var regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;
var string = "#ffbbad #Fc01DF #FFF #ffE";
console.log( string.match(regex) );
// => ["#ffbbad", "#Fc01DF", "#FFF", "#ffE"]
複製代碼
可視化形式以下:
按照 24 小時爲例。
要求匹配:
23:59
02:07
複製代碼
分析:
共4位數字,第一位數字能夠爲:[0-2].
當第一位爲"2"時,第二位能夠爲[0-3],其餘的狀況時,第二位爲[0-9].
第三位數字爲[0-5],第四位數字爲[0-9].
正則以下:
var regex = /^[01][0-9]|[2][0-3]:[0-5][0-9]$/;
console.log(regex.test("23:59"));
console.log(regex.test("02:07"));
// => true
// => true
複製代碼
注意:正則中使用到了 ^ 和 $ 分別表示字符串的開頭和結尾。
若是想要匹配7:9
說明時分前面的 0 能夠省略。
這個時候正則能夠變成:
var regex = /^(0?[0-9]|1[0-9]|[2][0-3]):(0?[0-9]|[1-5][0-9])$/;
console.log(regex.test("23:59"));
console.log(regex.test("02:07"));
console.log(regex.test("7:9"));
// => true
// => true
// => true
複製代碼
其可視化形式以下:
好比:yyyy-mm-dd 格式爲例子。 要求匹配:
2017-06-10
複製代碼
分析: 年:四位數字便可 可使用[0-9]{4} 月:共計12個月,分爲兩種狀況"01"、"02"、…… "09" 和 "10"、"11"、"12" 可使用 (0[1-9]1[0-2]). 日:最大31天, 可使用(0[1-9]|[12][0-9]|3[01]]).
正則以下:
var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
console.log( regex.test("2017-06-10") );
// => true
複製代碼
其可視化的形式以下:
要求匹配:
F:\study\javascript\regex\regular expression.pdf
F:\study\javascript\regex\
F:\study\javascript
F:\
複製代碼
分析: 總體的模式是:
盤符:\文件夾\文件夾\文件夾\
複製代碼
其中匹配:F:\
須要使用 [a-zA-Z]:\\
,其中 盤符不區分大小寫。注意\ 字符須要轉義.
文件名或者文件夾名 不能包含一些特殊字符、此時咱們須要使用經常使用的排除字符組:[^\\:*<>|"?\r\n/]
來表示合法字符。
另外它們名字不能爲空的名 至少有一個字符,也就是須要使用量詞 + 所以匹配文件夾\
的名稱可以使用 [^\\:*<>|"?\r\n/]+\\
。
另外 文件夾\
能夠出現任意次數.也就是([^\\:*<>|"?\r\n/]+\\)*
其中括號表示其內部正則是一個總體。
路徑的最後一個部分還能夠是文件夾 沒有 \ 所以須要添加 ([^\\:*<>|"?\r\n/]+)?
. 最後拼接成了一個正則:
var regex=/^[a-zA-Z]:\\([^\\:*<>|"?\r\n/]+\\)*([^\\:*<>|"?\r\n/]+)?$/
console.log(regex.test("F:\\study\\javascript\\regex\\regular expression.pdf"))
console.log( regex.test("F:\\study\\javascript\\regex\\"))
console.log( regex.test("F:\\study\\javascript"))
console.log( regex.test("F:\\"))
// => true
// => true
// => true
// => true
複製代碼
其中在JavaScript中字符串須要表示字符 \ 時候也須要轉義。
可視化爲:
要求從:
<div id="container" class="main"></div>
複製代碼
提取出 id="container".
可能最開始想到的正則是:
var regex = /id=".*"/
var string = '<div id="container" class="main"></div>'
console.log(string.match(regex)[0]);
// => id="container" class="main"
複製代碼
其可視化的形式是:
由於 . 是通配符 自己就是匹配雙引號,而量詞 * 又是貪婪的,當遇到 container 後面的雙引號時候,是不會 停下來,會繼續匹配,直到遇到最後一個雙引號爲止。
解決的辦法是使用惰性匹配:
var regex = /id=".*?"/
var string = '<div id="container" class="main"></div>'
console.log(string.match(regex)[0]);
// => id="container"
複製代碼
固然這樣也會有一個問題就是效率比較低,由於匹配原理會涉及到回溯的概念,能夠優化以下:
var regex = /id="[^"]*"/
var string = '<div id="container" class="main"></div>'
console.log(string.match(regex)[0]);
// => id="container"
複製代碼