正則表達式(Regular Expression)是計算機科學的一個概念。正則表達式使用單個字符串來描述、匹配一系列符合某個句法規則的字符串。在不少文本編輯器裏,正則表達式一般被用來檢索、替換那些符合某個模式的文本。正則表達式
JavaScript經過內置對象RegExp
支持正則表達式,有兩種方式建立正則表達式對象,若是咱們想匹配字符串中<%xxx%>
兩個百分號分割的字符串能夠這麼寫數組
1.構造函數bash
var reg=new RegExp('<%[^%>]+%>','g');
編輯器
2.字面量函數
var reg=/<%[^%>]%>/g;
測試
最後的g表明全局,還有幾個修飾符ui
1.g:global,全文搜索,不添加的話搜索到第一個結果中止搜索 2.i:ingore case,忽略大小寫,默認大小寫敏感 3.m:multiple lines,多行搜索url
正則表達式讓人望而卻步以一個重要緣由就是轉義字符太多了,組合很是多,可是正則表達式的元字符(在正則表達式中具備特殊意義的專用字符,能夠用來規定其前導字符)並很少spa
( [ { \ ^ $ | ) ? * + .
並非每一個元字符都有特定的意義,在不一樣的組合中元字符有不一樣的意義,分類看一下prototype
字符 | 含義 |
---|---|
\t | 水平製表符 |
\r | 回車符 |
\n | 換行符 |
\f | 換頁符 |
\v | 垂直製表符 |
\0 | 空字符 |
通常狀況下正則表達式一個字符(轉義字符算一個)對應字符串一個字符,表達式 hello的含義是 "hello"
可是咱們可使用元字符[]來構建一個簡單的類, 好比[abcd]表明一個字符,這個字符能夠是 abcd四個字符中的任意一個
元字符[]組合能夠建立一個類,咱們還可使用元字符^建立反向類/負向類,反向類的意思是不屬於XXX類的內容,表達式 [^abc] 表示一個不是字符a或b或c的字符
按照上面的說明若是但願匹配單個數字那麼表達式是這樣的
//匹配一個字符,這個字符能夠是0-9中的任意一個
var reg1 = /[0123456789]/
//匹配一個字符,這個字符能夠是0-9中的任意一個
var reg2 = /[0-9]/
//匹配一個字符,這個字符能夠是a-z中的任意一個
var reg3 = /[a-z]/
//匹配一個字符,這個字符能夠是大寫字母、小寫字母、數字中的任意一個
var reg3 = /[a-zA-Z0-9]/
複製代碼
字符 | 等價類 | 含義 |
---|---|---|
. | [^\r\n] | 除了回車符和換行符以外的全部字符 |
\d | [0-9] | 數字字符 |
\D | [^0-9] | 非數字字符 |
\s | [\t\n\x0B\f\r] | 空白符 |
\S | [^\t\n\x0B\f\r] | 非空白符 |
\w | [a-zA-Z_0-9] | 單詞字符,字母、數字下劃線 |
\W | [^a-zA-Z_0-9] | 非單詞字符 |
有了這些預約義類,寫一些正則就很方便了,好比咱們但願匹配一個能夠是 ab+數字+任意字符 的字符串,就能夠這樣寫了 /ab\d./
正則表達式還提供了幾個經常使用的邊界匹配字符
字符 | 含義 |
---|---|
^ | 以xxx開頭 |
$ | 以xxx結尾 |
\b | 單詞邊界 |
\B | 非單詞邊界 |
\t、\r、\n 也屬於單詞邊界
var str = 'hello1 world hello2 123456 \t \r jirengu \n ruoyu hello3'
str.match(/hello\d/g) // ["hello1", "hello2", "hello3"]
str.match(/^hello\d/g) // ["hello1"]
str.match(/hello\d$/g) // ["hello3"]
var str2 = 'hello1 whello9orld hello2 12-hello8-3456 \t \r jirengu \n ruoyu hello3'
str2.match(/\bhello\d\b/g) //["hello1", "hello2", "hello8", "hello3"]
//注意-也用於區分單詞邊界
複製代碼
測試題:
變量 className 爲頁面DOM元素對應的class 屬性字符串,如下代碼是檢測 className 中是否包含值爲"header"的 class,寫法是否正確,如不正確給出反例,並寫出正確代碼
var className = xxx
if(className.match(/\bheader\b/)){
console.log('has class: header')
}
複製代碼
答案: 以上寫法不正確,好比:
className = 'header3 clearfix active header-fixed'
,
此時頁面元素並無 header 這個 class,但上面的代碼會認爲有。是由於-也是單詞邊界
正確的寫法是:
var reg = /(^|\s)header($|\s)/g
以前咱們介紹的方法都是一一匹配的,若是咱們但願匹配一個連續出現20次數字的字符串難道咱們須要寫成這樣
\d\d\d\d... 爲此正則表達式引入了一些量詞
字符 | 含義 |
---|---|
? | 出現零次或一次(最多出現一次) |
+ | 出現一次或屢次(至少出現一次) |
* | 出現零次或屢次(任意次) |
{n} | 出現n次 |
{n,m} | 出現n到m次 |
{n,} | 至少出現n次 |
var str1 = 'http://jirengu.com'
str1.match(/https?:\/\/.+/) //匹配
str1.match(/https+:\/\/.+/) //不匹配
str1.match(/https*:\/\/.+/) //匹配
var str2 = 'https://jirengu.com'
str2.match(/https?:\/\/.+/) //匹配
str2.match(/https+:\/\/.+/g) //匹配
str2.match(/https*:\/\/.+/g) //匹配
var str3 = 'httpssssss://jirengu.com'
str3.match(/https?:\/\/.+/g) //不匹配
str3.match(/https+:\/\/.+/g) //匹配
str3.match(/https*:\/\/.+/g) //匹配
複製代碼
測試題:
如何匹配一個合法的 url?提示:url 以 http 或者 https 或者 // 開頭
var reg = /^(https?:)?\/\/.+/
看了上面介紹的量詞,也許愛思考的同窗會想到關於匹配原則的一些問題,好比{3,5}這個量詞,要是在句子中出現了十次,那麼他是每次匹配三個仍是五個,反正三、四、5都知足3~5的條件
量詞在默認下是儘量多的匹配的,也就是你們常說的貪婪模式
'123456789'.match(/\d{3,5}/g); //["12345", "6789"]
既然有貪婪模式,那麼確定會有非貪婪模式,讓正則表達式儘量少的匹配,也就是說一旦成功匹配再也不繼續嘗試,作法很簡單,在量詞後加上?便可
'123456789'.match(/\d{3,5}?/g); //["123", "456", "789"]
有時候咱們但願使用量詞的時候匹配多個字符,而不是像上面例子只是匹配一個,好比但願匹配Byron出現20次的字符串,咱們若是寫成 hunger{10}
的話匹配的是hunge+r
出現10次
/hunger{10}/
怎麼把hunger做爲一個總體呢?使用()就能夠達到此目的,咱們稱爲分組
/(hugner){10}/
var reg1 = /hello|world/
//等同於
var reg2 = /(hello)|(world)/
複製代碼
舉例:
從HTML字符串中取出 URL
var str = '<a href="http://jirengu.com">"飢人谷"</a>'
var reg = /href="((https?:)?\/\/.+?)"/
console.log(str.match(reg)) //["href="http://jirengu.com"", "http://jirengu.com", "http:"]
var url = str.match(reg)[1]
複製代碼
表達式 | 含義 |
---|---|
exp1(?=exp2) | 匹配後面是exp2的exp1 |
exp1(?!exp2) | 匹配後面不是exp2的exp1 |
有些抽象,看個例子
hunger(?=Byron)
(/good(?=Byron)/).exec('goodByron123'); //['good']
(/good(?=Byron)/).exec('goodCasper123'); //null
(/bad(?=Byron)/).exec('goodCasper123');//null
複製代碼
經過上面例子能夠看出 exp1(?=exp2)
表達式會匹配exp1表達式,但只有其後面內容是exp2的時候纔會匹配,也就是兩個條件,exp1(?!exp2) 比較相似
good(?!Byron)
(/good(?!Byron)/).exec('goodByron123'); //null
(/good(?!Byron)/).exec('goodCasper123'); //['good']
(/bad(?!Byron)/).exec('goodCasper123');//null
複製代碼
RegExp實例對象有五個屬性
global:是否全局搜索,默認是false
ignoreCase:是否大小寫敏感,默認是false
multiline:多行搜索,默認值是false
lastIndex:是當前表達式模式首次匹配內容中最後一個字符的下一個位置,每次正則表達式成功匹配時,lastIndex屬性值都會隨之改變
source:正則表達式的文本字符串
除了將正則表達式編譯爲內部格式從而使執行更快的compile()方法,對象還有兩個咱們經常使用的方法
方法用於測試字符串參數中是否存正則表達式模式,若是存在則返回true,不然返回false
var reg = /\d+\.\d{1,2}$/g;
reg.test('123.45'); //true
reg.test('0.2'); //true
reg.test('a.34'); //false
reg.test('34.5678'); //false
複製代碼
方法用於正則表達式模式在字符串中運行查找,若是exec()找到了匹配的文本,則返回一個結果數組,不然返回 null
除了數組元素和length屬性以外,exec()方法返回對象還包括兩個屬性。
1.index 屬性聲明的是匹配文本的第一個字符的位置 2.input 屬性則存放的是被檢索的字符串string
調用非全局的RegExp對象的exec()時,返回數組的第一個元素是與正則表達式相匹配的文本,第二個元素是與 RegExpObject的第一個子表達式相匹配的文本(若是有的話),第三個元素是與RegExp對象的第二個子表達式相匹配的文本(若是有的話),以此類推。
調用全局的RegExp對象的exec()時,它會在RegExp實例的lastIndex屬性指定的字符處開始檢索字符串string
1.當exec()找到了與表達式相匹配的文本時,在匹配後,它將把RegExp實例的lastIndex屬性設置爲匹配文本的最後一個字符的下一個位置。能夠經過反覆調用exec()方法來遍歷字符串中的全部匹配文本
2.當 exec() 再也找不到匹配的文本時,它將返回null,並把lastIndex屬性重置爲0
var reg = /\d/g;
var r = reg.exec('a1b2c3');
console.log(reg.lastIndex); //2
r=reg.exec('a1b2c3');
console.log(reg.lastIndex); //4
var reg=/\d/g;
while(r=reg.exec('a1b2c3')){
console.log(r.index+':'+r[0]);
}
複製代碼
能夠看到結果
1:1
3:2
5:3
複製代碼
除了上面的兩個方法,有些字符串函數能夠傳入RegExp對象做爲參數,進行一些複雜的操做
search() 方法用於檢索字符串中指定的子字符串,或檢索與正則表達式相匹配的子字符串
search() 方法不執行全局匹配,它將忽略標誌g,它同時忽略正則表達式對象的lastIndex屬性,而且老是從字符串的開始進行檢索,這意味着它老是返回字符串的第一個匹配的位置
'a1b2c3'.search(/\d/g); //1
'a1b2c3'.search(/\d/); //1
複製代碼
match()方法將檢索字符串,以找到一個或多個與regexp匹配的文本。但regexp是否具備標誌 g對結果影響很大。
若是regexp沒有標誌g,那麼match()方法就只能在字符串中執行一次匹配。若是沒有找到任何匹配的文本,match() 將返回null。不然它將返回一個數組,其中存放了與它找到的匹配文本有關的信息。
該數組的第一個元素存放的是匹配文本,而其他的元素存放的是與正則表達式的子表達式匹配的文本。除了這些常規的數組元素以外,返回的數組還含有兩個對象屬性
1.index 屬性聲明的是匹配文本的起始字符在字符串中的位置
2.input 屬性聲明的是對 stringObject 的引用
看個例子
var r = 'aaa123456'.match(/\d/);
若是regexp具備標誌g則match()方法將執行全局檢索,找到字符串中的全部匹配子字符串
若沒有找到任何匹配的子串,則返回 null。若是找到了一個或多個匹配子串,則返回一個數組
不過全局匹配返回的數組的內容與前者大不相同,它的數組元素中存放的是字符串中全部的匹配子串,並且也沒有index屬性或input屬性。
var r = 'aaa123456'.match(/\d/g);
關於string對象的replace方法,咱們最經常使用的是傳入兩個字符串的作法,但這種作法有個缺陷,只能replace一次
'abcabcabc'.replace('bc','X'); //aXabcabc
replace方法的第一個參數還能夠傳入RegExp對象,傳入正則表達式可使replace方法更增強大靈活
'abcabcabc'.replace(/bc/g,'X'); //aXaXaX
'abcaBcabC'.replace(/bc/gi,'X'); //aXaXaX
複製代碼
若是replace方法的第一個參數傳入的是帶分組的正則表達式,咱們在第二個參數中可使用$1...$9
來獲取相應分組內容,好比但願把字符串 1<%2%>34<%567%>89
的<%x%>
換爲$#x#$
,咱們能夠這樣
'1<%2%>34<%567%>89'.replace(/<%(\d+)%>/g,'@#$1#@');
//"1@#2#@34@#567#@89"
複製代碼
固然還有不少方式能夠達到這一目的,這裏只是演示一下利用分組內容,咱們在第二個參數中使用 @#$1#@
,其中$1
表示被捕獲的分組內容,在一些js模板函數中能夠常常見到這種方式替換字符串
能夠經過修改replace方法的第二個參數,使replace更增強大,在前面的介紹中,只能把全部匹配替換爲固定內容,但若是我但願把一個字符串中全部數字,都用小括號包起來該怎麼弄
'2398rufdjg9w45hgiuerhg83ghvif'.replace(/\d+/g,function(r){
return '('+r+')';
}); //"(2398)rufdjg(9)w(45)hgiuerhg(83)ghvif"
複製代碼
把replace方法的第二個參數傳入一個function,這個function會在每次匹配替換的時候調用,算是個每次替換的回調函數,咱們使用了回調函數的第一個參數,也就是匹配內容,其實回調函數一共有四個參數
看個例子
'2398rufdjg9w45hgiuerhg83ghvif'.replace(/\d+/g,function(a,b,c){
console.log(a+'\t'+b+'\t'+c);
return '('+a+')';
});
複製代碼
結果是
2398 0 2398rufdjg9w45hgiuerhg83ghvif
9 10 2398rufdjg9w45hgiuerhg83ghvif
45 12 2398rufdjg9w45hgiuerhg83ghvif
83 22 2398rufdjg9w45hgiuerhg83ghvif
複製代碼
這是沒有分組的狀況,打印出來的分別是 匹配內容、匹配項index和原字符串,看個有分組的例子,若是咱們但願把一個字符串的<%%>
外殼去掉,<%1%><%2%><%3%>
變成123
'<%1%><%2%><%3%>'.replace(/<%([^%>]+)%>/g,function(a,b,c,d){
console.log(a+'\t'+b+'\t'+c+'\t'+d);
return b;
}) //123
複製代碼
結果是
<%1%> 1 0 <%1%><%2%><%3%>
<%2%> 2 5 <%1%><%2%><%3%>
<%3%> 3 10 <%1%><%2%><%3%>
複製代碼
根據這種參數replace能夠實現不少強大的功能,尤爲是在複雜的字符串替換語句中常用
咱們常用split方法把字符串分割爲字符數組
'a,b,c,d'.split(','); //["a", "b", "c", "d"]
和replace方法相似,在一些複雜的分割狀況下咱們可使用正則表達式解決
'a1b2c3d'.split(/\d/); //["a", "b", "c", "d"]