本文內容主要出處為《JavaScript權威指南》(第六版),筆者只是在搬磚的同時整理思路,有誤望及時指出,感謝!
定義正規表示式
概述
對於正規表示式的概念我們就不多費口舌了...
在JavaScript中使用正規表示式進行模式匹配離不開RegExp
物件,建立正則物件有兩種方式
1.正規表示式直接量(包含在一對/
之間的字元)
var reg = /java/;
reg.test('java'); // true複製程式碼
2.new RegExp()
var reg = new RegExp('java');
reg.test('java'); // true複製程式碼
JavaScript也賦予了String
物件進行模式匹配的方法,但是使用這些方法的時候同樣離不開RegExp
例如:
var str = 'java';
str.match(/java/); // /java/為正則直接量複製程式碼
String
與 RegExp
如何進行正則匹配,有哪些方法,會在下面兩節講解,這一節(定義正規表示式),主要講述如何定義正規表示式(本節會用到RegExp
的 test
和 exec
方法,如若不瞭解,可以與最後一節結合來理解)
正則——直接字元量
直接字元量表示字元在正規表示式中的表達形式。特殊字元需\
轉義
字母、數字 -> 自身
\o -> NUL字元(\u0000)
\t -> 製表符(\u0009)
\n -> 換行符(\u000A)
\v -> 垂直製表符(\u000B)
\f -> 換頁符(\u000C)
\r -> 回車符(\u000D)
...複製程式碼
正則——字元類
將字元量放入 []
中就成了字元類,字元類匹配它包含的任意一個字元
[abc] // 表示a 或者b 或者c
[^abc] // ^在這裡表示取反,除了a、b、c之外的所有字元
[a-z] //-表示連線,從a到z的任意字元複製程式碼
由上面我們可以知道,[0-9]
表示任意數字,像這種常用的字元類,JavaScript給他們制訂了自己的特殊轉義字元。
. // 除換行符與其他Unicode行終止符以外的任意字元
\w // 等價於[a-zA-Z0-9_],大小寫字母、數字、下劃線63字元中任意一個
\W // 等價於[^a-zA-Z0-9_]
\s // 任何Unicode空白符
\S // 任何Unicode非空白符
\d // 等價於[0-9]
\D // 等價於[^0-9]
[\b] // \b放入[]中標識退格直接量複製程式碼
正則——重複
描述相同字元,多次出現
{n, m} // 最少重複n次,最多重複m次
{n, } // 至少重複n次
{n} //重複n次
? // 等價於 {0, 1}
+ // 等價於 {1,}
* // 等價於 {0,}複製程式碼
例如:
var reg = new RegExp('a{2,}');
var str = 'aaa';
var str2 = 'a';
reg.test(str); // true
reg.test(str2); // false複製程式碼
在這裡會多出一個概念:非貪婪重複
在上述例子中,reg.test(str) === true
我們用reg.exec(str)
獲取匹配結果會發現 結果為'aaa'
如果我們選擇使用非貪婪重複
var reg = new RegExp('a{2,}?');
var str = 'aaa';
reg.exec(str);複製程式碼
這時候結果為 'aa'
,實現非貪婪重複很簡單,在重複後面新增?即可,這時候,正規表示式會盡可能少的去匹配重複。
{n, m} -> {n, m}?
{n, } -> {n,}?
{n} -> {n}?
? -> ??
+ -> +?
* -> *?複製程式碼
選擇
用 |
可以分割用於選擇的字元,優先順序從左向右
ab|cd|ef // 表示 ab 或者 cd 或者 ef複製程式碼
子模式
()
在包裹子表示式同時將其定義為子模式
我們可以通過 \index
這種寫法在同一個正規表示式中呼叫子模式, index
表示子模式的索引,從1開始。
var reg1 = /(java)script and \1/;
var reg2 = /javascript and java/;
// reg1 與 reg2 是兩個基本等價的正則直接量
var str = 'javascript and java';
reg1.test(str); // true
reg2.test(str); // true複製程式碼
子模式還有助於我們抽取子表示式匹配結果
把上述例子改為用exec
方法並且列印
var reg1 = /(java)script and \1/;
var reg2 = /javascript and java/;
var str = 'javascript and java';
console.log(reg1.exec(str));
console.log(reg2.exec(str));複製程式碼
輸出結果為:
可以看到,當存在子模式時候,結果集會新增其子模式匹配結果,從中我們也可以看出,為什麼子模式index
的索引會從1開始,因為0是完整的正則匹配結果。
當然,JavaScript允許我們在使用子表示式的時候不生成子模式
使用(?: )
來包裹子表示式
var reg = /(?:java)script and java/;複製程式碼
此時我們無法通過\1
找到其子模式,也無法獲取其子模式的匹配結果。
指定匹配位置
^ // 字串開始位置(在字元類中表示取反)
$ // 字串的結束位置
\b // 單詞邊界,也就是\w與\W的邊界
(?=p) // 要求字串與p匹配,但是結果集並不包含匹配p的字元
(?!p) // 要求字串不與p匹配複製程式碼
^ 與 $
/^javascript/ // 字串以javascript開始
/javascript$/ // 字串以javascript結束複製程式碼
\b
var reg = /\bjava\b/;
var str1 = 'java';
var str2 ='javascript';
var str3 = 'java c++';
var str4 = 'html java c++';
reg.test(str1); // true
reg.test(str2); // false
reg.test(str3); // true
reg.test(str4); // true
在這裡 \b 匹配非\w字元,包括字串起始與結束。
\B與之相反,匹配非單詞邊界處複製程式碼
(?=)
var reg = /java(?=script)/;
var str = 'java';
var str1 = 'javascript';
reg.exec(str); // 匹配失敗 , 因為不包含script
reg.exec(str1); // 此時匹配成公,但是匹配結果並不包含script複製程式碼
輸出結果為:
(?!)
var reg = /java(?!script)/;
var str = 'javaee';
var str1 = 'javascript';
reg.exec(str); // 匹配成功,匹配結果為java
reg.exec(str1); // 匹配失敗,因為包含script複製程式碼
修飾符
i // 不區分大小寫
m // 匹配多行(使用^ $指定起止時候能通融\n換行)
g // 匹配成功第一處,並不會繼續停止,會繼續尋找所有匹配複製程式碼
通過直接量建立正則物件新增修飾符: /java/gim
(使用多個修飾詞直接並列)
通過建構函式建立正則物件新增修飾符: new RegExp(reg , 'gim');
通過String的模式匹配
String物件提供四種方法用於正則匹配。
search()
str.search(reg)
匹配成功返回起始位置,失敗返回-1
,在search
方法中修飾詞g
不生效
var str = 'hello java';
str.search(/java/); // 6
str.search(/^java/); // -1複製程式碼
match()
str.match(reg)
匹配失敗返回null
,匹配成功返回的是一個由匹配結果組成的陣列。如果該正則 表示式設定了修飾符g
,則該方法返回的陣列包含字串中的所有匹配結果。
var str = 'hello java and javascript';
str.match(/java/);
str.match(/java/g);複製程式碼
輸出結果為:
replace()
兩種呼叫方式,第二個引數不同
str.replace(reg , replaceStr)
var str = 'javaee javaee'; // str1 = 'javascript javaee' var str1 = str.replace(/e{2}/ , 'script'); // str2 = 'javascript javascript' 修飾符g表示全域性替換 var str2 = str.replace(/e{2}/g , 'script'); // 補充知識點:str 依然是 javaee ,至於原因,簡單說就是字串直接量不可改變,字串所有的有關於值的改變的方法都是return新值。複製程式碼
第二個引數replaceStr,用於替換的字串有一些特殊的寫法
var reg = /"([^"]*)"/g; // 匹配 "" 間的內容,且內容不包含" var str = '"java","c++" and "html"'; var str1 = str.replace(reg , '“$1”'); console.log(str1) // “java”,“c++” and “html”複製程式碼
此時,
$1
表示子模式([^"]*)
匹配的結果集, 與我們上一節定義正規表示式中的呼叫自己的子模式\1
相仿。
同樣,在此處$
還有其他幾種用法$index // 就是上述例子中的$1 $& // 表示原字串 -> "java","c++" and "html" $` // 匹配字串的左側值,在上述例子中匹配成功三次,分別為: 空值 、 "java", 、"java","c++" and $' // 匹配字串的右側值,在上述例子中匹配成功三次,分別為: ,"c++" and "html" 、 and "html" 、空值 $$ // 字元常量$複製程式碼
str.replace(reg , function)
var reg = /"([^"]*)"/g; // 匹配 "" 間的內容,且內容不包含" var str = '"java","c++" and "html"'; var str1 = str.replace(reg , function (...arr) { console.log(arr) });複製程式碼
輸出結果為:
匹配成功了三次,所以輸出了三次
arr
陣列,這個陣列裡面的元素分別為:0
: 匹配結果...
: 若存在子模式,這裡為子模式匹配結果last - 1
: 索引位置last
: 原字串我們可以根據自己想要的結果,動態替換內容,通過
return
將結果替換到新的字串中。var reg = /"([^"]*)"/g; // 匹配 "" 間的內容,且內容不包含" var str = '"java","c++" and "html"'; var str1 = str.replace(reg , function (...arr) { return `“${arr[1]}”` }); console.log(str1) // “java”,“c++” and “html”複製程式碼
split()
str.split(分隔符)
根據分隔符分割返回新陣列str.split(reg)
根據正則分割返回新陣列var str = 'a,b-c,e,f-g'; var arr = str.split(/[,-]/); console.log(arr) // ['a','b','c','d','e','f','g']複製程式碼
通過RegExp的模式匹配
建構函式
var reg = new RegExp('\\w'); // 通過正規表示式字串
var reg1 = new RegExp(/\w/); // 通過正規表示式直接量
var reg2 = new RegExp(reg , 'gim'); // 第二個引數為修飾符複製程式碼
屬性
source
:String
正規表示式文字值global
:Boolean
是否攜帶全域性修飾符g
ignoreCase
:Boolean
是否攜帶忽略大小寫修飾符i
multiline
:Boolean
是否攜帶匹配多行修飾符m
lastIndex
:Number
如果global === true
, 那麼這個引數記錄每次匹配後的索引位置,下面的test
和exec
方法會用到
方法
reg.exec(str)
如果匹配失敗,返回null
,匹配成功,返回匹配結果,每次執行僅返回一個匹配結果,若global === true
,每次匹配成功後,會把lastIndex
屬性設定為緊挨著 匹配子串的字元位置。再次呼叫此方法,會從當前位置開始匹配(當我們使用同一個RegExp
匹配新的字串的時候,最好把lastIndex
屬性設定為0)
輸出結果為:var reg = new RegExp('java' , 'g'); var str = 'javascript java javaee'; var result = reg.exec(str); while (result) { console.log(result) console.log(`lastIndex = ${reg.lastIndex}`) result = reg.exec(str); }複製程式碼
當我們通過正規表示式直接量來呼叫進行正則運算的時候,並不會出現這種情況,那是因為在ES5中,每次通過直接量進行正則運算,JavaScript都會生成新的RegExp
物件。
reg.test(str)
test
方法與exec
基本等價,不同的是他們的返回值,test比較簡單粗暴,當exec
返回null
時候,它返回false
,其他情況返回true