正則表達式


含義


正則表達式(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()方法,對象還有兩個咱們經常使用的方法


RegExp.prototype.test(str)


方法用於測試字符串參數中是否存正則表達式模式,若是存在則返回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
複製代碼

RegExp.prototype.exec(str)


方法用於正則表達式模式在字符串中運行查找,若是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對象做爲參數,進行一些複雜的操做


String.prototype.search(reg)


search() 方法用於檢索字符串中指定的子字符串,或檢索與正則表達式相匹配的子字符串

search() 方法不執行全局匹配,它將忽略標誌g,它同時忽略正則表達式對象的lastIndex屬性,而且老是從字符串的開始進行檢索,這意味着它老是返回字符串的第一個匹配的位置

'a1b2c3'.search(/\d/g); //1
'a1b2c3'.search(/\d/); //1
複製代碼

String.prototype.match(reg)


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.prototype.replace(reg, replaceStr)


關於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模板函數中能夠常常見到這種方式替換字符串


String.prototype.replace(reg, function)


能夠經過修改replace方法的第二個參數,使replace更增強大,在前面的介紹中,只能把全部匹配替換爲固定內容,但若是我但願把一個字符串中全部數字,都用小括號包起來該怎麼弄

'2398rufdjg9w45hgiuerhg83ghvif'.replace(/\d+/g,function(r){
    return '('+r+')';
}); //"(2398)rufdjg(9)w(45)hgiuerhg(83)ghvif"
複製代碼

把replace方法的第二個參數傳入一個function,這個function會在每次匹配替換的時候調用,算是個每次替換的回調函數,咱們使用了回調函數的第一個參數,也就是匹配內容,其實回調函數一共有四個參數

  • 第一個參數很簡單,是匹配字符串
  • 第二個參數是正則表達式分組內容,沒有分組則沒有該參數
  • 第三個參數是匹配項在字符串中的index
  • 第四個參數則是原字符串

看個例子

'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能夠實現不少強大的功能,尤爲是在複雜的字符串替換語句中常用


String.prototype.split(reg)


咱們常用split方法把字符串分割爲字符數組

'a,b,c,d'.split(','); //["a", "b", "c", "d"]

和replace方法相似,在一些複雜的分割狀況下咱們可使用正則表達式解決

'a1b2c3d'.split(/\d/); //["a", "b", "c", "d"]

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息