一篇文章搞定 javascript 正規表示式

chroot發表於2019-03-02

前言

正規表示式在平時工作中非常常見,但是它的神奇對於很多程式設計師可能一直像魔法一樣的存在,工作中用到的大部分正則都是去網上搜尋得來的,再複雜一點看著文件費時費力的拼湊一下。是不是深有感觸了?一次在網上看到有關正則的視訊,讓我收貨頗多,當時認真記錄了筆記和自己的感悟,也希望給更多需要的童鞋帶來幫助。文章從零開始講正則,導致有點長,可以收藏零碎時間慢慢看,認真看完絕對受益匪淺。文章首發於我的部落格,以下是正文:

Regular Expression 使用單個字串來描述、匹配一系列符合某個語法規則的字串。說簡單了就是按照某種規則去匹配符合條件的字串。這裡先推薦一個學習正規表示式的線上工具:regexper.com/,網站利用影象和英文解…^\d{4}[/-]\d{2}[/-]\d{2}$在工具中是這樣顯示的:

一篇文章搞定 javascript 正規表示式

是不是非常直觀~

RegExp物件

javaScript中通過內建物件 RegExp 支援正規表示式,有兩種方法例項化 RegExp 物件:

  1. 字面量
  2. 建構函式

1. 字面量

假設你需要把一句英文裡面的小寫is匹配成大寫的 IS,你可以這樣做:

var reg = /\bis\b/;
var text = 'He is a boy, This is a dog. Where is she?';
var result = text.replace(reg,'IS');
console.log(result) //He IS a boy, This is a dog. Where is she?
複製程式碼

這樣就把第一個英文單詞'is'替換成了'IS',假如你想把該句中所有的單詞'is'都替換成'IS',應該這樣寫:

var reg = /\bis\b/g;
var text = 'He is a boy, This is a dog. Where is she?';
var result = text.replace(reg,'IS');
console.log(result) //He IS a boy, This IS a dog. Where IS she?
複製程式碼

在正則的末尾加上'g'就好,'g'表示global,是全域性匹配的意思。'g'是正規表示式的一個修飾符,修飾符有:

  • 'g': global 全文搜尋,不新增,搜尋到第一個停止
  • 'i': ignore case 忽略大小寫,預設大小寫敏感
  • 'm': multiple 多行搜尋 ,檢測字串中的換行符,主要是影響字串開始識別符號^和結束識別符號$的使用

可能你會想,為什麼句子中的'This'中的is沒有被匹配成功呢,這就是我們'\b'的功勞了。

'\b':匹配一個單詞邊界,也就是指單詞和空格間的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。

這裡的正則在'is'的前後都有'\b',這樣就只能匹配單詞'is'了。

建構函式

倘若你需要使用建構函式的方式例項化正則,則上面的字面量形式可以改成這樣:

var reg = new RegExp('\\bis\\b','g');
var text = 'He is a boy, This is a dog. Where is she?';
var result = text.replace(reg,'IS');
console.log(result) //He IS a boy, This IS a dog. Where IS she?
複製程式碼

用這種方式就不需要'/'符號開始和結尾以表示是正則了。但是裡面的'\'等特殊字元需要用'\'轉義。

'\':將下一個字元標記為一個特殊字元、或一個原義字元、或一個向後引用、或一個八進位制轉義符。例如,“n”匹配字元“n”。“\n”匹配一個換行符。序列“\\”匹配“\”而“\(”則匹配“(”。

元字元

正規表示式由兩種基本字元型別組成:

  • 原義文字字元,即代表它原本含義的字元
  • 元字元,元字元是在正規表示式中有特殊含義的非字母字元,例如上文提到的'\b',表示匹配單詞邊界,並不是匹配'\b',在正則中主要存在這些特殊字元:*,+,?,$,^,.,|,\,(,),{,},[,]

字元類(字符集合)

一般情況下,正規表示式一個字元對應字串一個字元,例如:ab\t就是匹配字串'ab'+'tab'

但是更多的時候,我們匹配的並不是某個字元,而是符合一系列特徵的字串。這時候,我們就可以使用元字元'[]'來構建一個簡單的類,所謂類是指符合某些特性的物件,一個泛指,而不是特指某個字元,例如:表示式'[abc]'把字元a或b或c歸為一類,表示式可以匹配這樣的字元。

var reg = /[abc]/g;
var text = 'a1b2c3d4';
var result = text.replace(reg,'X');
console.log(result); //X1X2X3d4
複製程式碼

這樣我們匹配的就是不是abc這樣三個字元了,而是abc中任何一個字元,這就是元字元的一個應用。

字元類取反

使用元字元'^'建立 反向類/負向類

反向類的意思是不屬於類的內容,表示式'[^abc]'表示不是字元a或b或c的內容,例如:

var reg = /[^abc]/g;
var text = 'a1b2c3d4';
var result = text.replace(reg,'X');
console.log(result); //aXbXcXXX
複製程式碼

範圍類

倘若我們需要用字元類匹配數字,按照前面的匹配方式,書寫可能會很麻煩,需要這樣:'[0123456789]',對於 a 到 z 的字元更是如此。

為此,正規表示式給我們提供了範圍類,我們可以使用[a-z]來連線兩個字元,表示從a到z的任意字元,這是一個閉區間,包含 a 和 z 本身。

var reg = /[a-z]/g;
var text = 'a1b2c3d4z9';
var result = text.replace(reg,'Q');
console.log(result); //Q1Q2Q3Q4Q9
複製程式碼

可以發現,這樣就方便了許多。

此外,在'[]'組成的類的內部是可以連寫的[a-zA-Z],這樣就形成了大寫字母小寫字母完全匹配:

var reg = /[a-zA-Z]/g;
var text = 'a1b2c3d4z9ASDFHDFH';
var result = text.replace(reg,'Q');
console.log(result); //Q1Q2Q3Q4Q9QQQQQQQQ
複製程式碼

有些童鞋可能會想,我想連範圍類裡面的'-'字元也一起匹配了,我們該怎麼做?

其實也很簡單,例如:

var reg = /[0-9]/g; //這樣是跟前面一樣的結果,不行
var text = '2018-05-13';
var result = text.replace(reg,'Q');
console.log(result); //QQQQ-QQ-QQ

var reg = /[0-9-]/g; //只要在後面另外加一個‘-’符號就可以了
var text = '2018-05-13';
var result1 = text.replace(reg,'Q');
console.log(result1); //QQQQQQQQQQ
複製程式碼

預定義類及邊界

預定義類

正規表示式提供預預定義類來匹配常見的字元類,讓我們書寫更方便。

字元 等價類 含義
. [^\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] 非單詞字元

digit 數字 '\d', space空白 '\s',word字母 '\w',大寫取反,媽媽再也不用擔心我記錯了

來看一個實際的例子,匹配一個 ab+數字+任意字元 的字串:

var reg = /ab\d./; //之前我們可能會這樣寫:ab[0-9][^\r\n]
var text = 'absdlkjflab91323';
var result = text.replace(reg,'AAAA');
console.log(result); //absdlkjflAAAA323
複製程式碼

邊界

除了預定義類,正規表示式還提供了幾個常用的邊界字元。

字元 等價類
^ 以xxx開始
$ 以xxx結束
\b 單詞邊界
\B 非單詞邊界

我們在第一個例子中用到過'\b'單詞邊界,這裡我們做一個跟上面第一個例子相反的,只把'This'中的'is'替換為'IS'

var reg = /\Bis\b/g;
var text = 'He is a boy, This is a dog. Where is she?';
var result = text.replace(reg,'IS');
console.log(result) //He is a boy, ThIS is a dog. Where is she?
複製程式碼

然後我們在說一下'^'和'$',在類'[]'中'^'表示取反,但是不在類中的時候'^'表示以xxx開始,'$'表示以xxx結束,這兩個邊界字元一般放在正則的開始和結束位置。

//先看沒加^或$的情況
var reg = /@./g;
var text = '@123@ab@A';
var result = text.replace(reg,'Q');
console.log(result); //Q23QbQ

//新增^的情況
var reg = /^@./g;
var text = '@123@ab@A';
var result1 = text.replace(reg,'Q');
console.log(result1); //Q23@ab@A

//新增$的情況
var reg = /@.$/g;
var text = '@123@ab@A';
var result1 = text.replace(reg,'Q');
console.log(result1); //@123@abQ
複製程式碼

上面的例子,如果'^'和'$'都加上的話,是匹配不成功的,因為沒有符號的字串可以匹配成功,童鞋們可以自己試試。

這裡再結合多行匹配舉一個例子:

var reg = /^@\d./g;
var text= '@123\n@456\n@789';
var result = text.replace(reg,'Q');
console.log(result);// Q3  @456  @789
複製程式碼

這裡你會發現,並沒有像我們預期的把三行中符合預期的字元都替換成功,只有第一行成功匹配替換了,這是為什麼呢?

這是因為,換行符在我們看來是換了一行寫而已,但是對於程式處理字串的時候,換行符就是一個普通的字元,並不算是我們認為的新的一行。這時候我們的修飾符'm'就可以大展身手了:

var reg = /^@\d./gm;
var text= '@123\n@456\n@789';
var result = text.replace(reg,'Q');
console.log(result);// Q3  @6  @9
複製程式碼

量詞

倘若我們希望匹配一個連續出現20次的數字的字串,通過我們之前學習的知識,我們可能會寫出連續20個'\d'。假如20次你還可以接受,那100次,1000次,甚至更多次,你怎麼辦?

為了解決這個問題,正規表示式引入了量詞的概念,下面是一些量詞和他們的含義:

字元 含義
出現零次或一次(最多出現一次)
+ 出現一次或者多次(至少出現一次)
* 出現零次或者多次(任意次)
{n} 出現n次
{n,m} 出現n到m次
{n,} 至少出現n次

我們可以拿文章開始的日期正則舉個栗子:

var reg = /\d{4}[/-]\d{2}[/-]\d{2}/g;
var text = '2018-02-23,2018/02/24,2018~02/25';
var result = text.replace(reg,'匹配正確日期格式');
console.log(result);//匹配正確日期格式,匹配正確日期格式,2018~02/25
複製程式碼

貪婪模式

正規表示式預設是貪婪模式,即每次匹配都儘可能的匹配多的字元,直到匹配失敗為止。

舉個栗子:

var reg = /\d{3,6}/g;
var text = '12345678';
var result = text.replace(reg,'X');
console.log(result);//X78
複製程式碼

從上面可以看出,正規表示式匹配了'123456',而不是'123','1234','12345',儘可能多的匹配了6次,即貪婪模式。

倘若我們希望它只匹配3次,即儘可能少的匹配,一旦匹配成功不再繼續嘗試,即非貪婪模式需要怎麼做呢?

很簡單,在量詞後面加上'?'即可,我們再用剛才的例子試一下:

var reg = /\d{3,6}?/g;
var text = '12345678';
var result = text.replace(reg,'X');
console.log(result);//XX78
複製程式碼

分組

假如我們有這麼一個場景:匹配字串 Byron 連續出現3次的場景,根據前面所學,我們可能會這樣寫:Byron{3}

但是這樣是錯誤的,試試你會發現只有Byronnn才能匹配成功,即最後的n重複了3次,並不能匹配整個單詞重複三次的情況:

var reg = /Byron{3}/g;
var text = 'ByronByronByronnn';
var result = text.replace(reg,'0');
console.log(result);//ByronByron0
複製程式碼

那麼,我們要怎麼匹配Byron連續出現3次的情況呢,這時候,正規表示式的分組'()'就幫我們解決了這個問題:

var reg = /(Byron){3}/g;
var text = 'ByronByronByronnn';
var result = text.replace(reg,'0');
console.log(result);//0nn
複製程式碼

有時候,我們可能會需要在匹配時用到或者的關係,利用之前的'[]'字元類(字符集合)可能只能匹配單個字元的或者關係,比如匹配a或b,你可以這樣寫:'[ab]',但是如果你需要匹配的是一整個單詞的或者關係呢,可能'[]'就不好使了。這時候,我們用'|'可以達到或的效果:

//匹配單詞Byron或者Casper
var reg = /Byron|Casper/g;
var text = 'ByronCasper'
var result = text.replace(reg,'X');
console.log(result);//XX

//匹配Byr+on或Ca+sper
var reg = /Byr(on|Ca)sper/g;
var text = 'ByronsperByrCasper'
var result1 = text.replace(reg,'X');
console.log(result1);//XX
複製程式碼

反向引用

假如我們有這樣一個需求:把日期'2015-12-25'替換成'12/25/2015',如果是你,你現在會怎麼做呢?

你可能會這樣寫:

var reg = /\d{4}-\d{2}-\d{2}/g;
var text = '2015-12-25'
var result = text.replace(reg,'12/25/2015');
console.log(result);//12/25/2015
複製程式碼

但是上面這樣的寫法,你只能夠匹配到'2015-12-25'了,不能再匹配別的日期了,'2015-12-25'是會變的,這樣就達不到需求了。

這時候,正則的反向引用就可以取到作用了。表示式在匹配時,表示式引擎會將小括號 "( )" 包含的表示式所匹配到的字串記錄(分組捕獲)下來。在獲取匹配結果的時候,小括號包含的表示式所匹配到的字串可以單獨獲取。

在js中正則匹配成功的字串可以用$1表示第一次匹配成功,$3表示第三次匹配成功的字元,以此類推至$99)。於是,上面的例子就可以這樣寫了:

var reg = /(\d{4})-(\d{2})-(\d{2})/g;
var text = '2015-12-25'
var result = text.replace(reg,'$2/$3/$1');
console.log(result);//12/25/2015
複製程式碼

忽略分組

在上面的反向引用中,我們預設是根據'()'全部捕獲記錄為$1~$99的,倘若我們想忽略某個捕獲要怎麼辦呢?

不希望捕獲某些分組,只需要在分組內加上'?:'就可以了。

var reg = /(?:Byron)(\d{4})-(\d{2})-(\d{2})/g;
var text = 'Byron2016-12-05'
var result = text.replace(reg,'$2/$3/$1');
console.log(result);//12/05/2016
複製程式碼

這時候的$1不是Byron,而是2016了。

前瞻

正規表示式從文字頭部向尾部開始解析,文字尾部方向稱為“前”,前瞻就是在正規表示式匹配到規則的時候,向前檢查是否符合斷言,後顧/後瞻方向相反。

符合和不符合斷言稱為肯定/正向匹配和否定/負向匹配。

上面是前瞻的概念,是不是看完有點暈?我看完也有點暈...我來解釋一下:假如你需要匹配一個名字叫“張三”的人,以前我們可能是從一堆人中找出名字是“張三”的揪出來就行了,但是前瞻就是要求你,揪出的人的名字叫“張三”還不夠,還必須“張三”的父親必須叫“張二”或者其它特定條件,這樣就是前瞻了,類似的,後瞻就是名字是張三還不夠,兒子還必須叫“小張”等。

是不是有點明白了?不過需要注意的是,在javascript中是不支援後顧/後瞻的,所以我們也不需要關心了。(這裡糾正一下,在 ES2018(ES9) 中已經支援後瞻和命名分組了)

至於符合/不符合斷言,可以解釋為:比如匹配要求名字叫“張三”,並且呢他父親不叫“張二”,對於符合的我們就叫正向/肯定匹配,不符合的就叫負向/否定匹配。

我們再用表格說明一下:

名稱 正則 含義
正向前瞻 exp(?=assert) 我們匹配符合了exp部分的表示式,然後還不算完,必須也匹配斷言部分('()'內部,'='後面的正則),才算成功
負向前瞻 exp(?!assert) 我們匹配符合了exp部分的表示式,然後還不算完,必須也匹配斷言部分('()'內部,'!'後面的正則),才算成功
正向後顧 exp(?<=assert) javascript不支援
負向後顧 exp(?<!assert) javascript不支援

現在是不是清楚了?如果還不夠明白,沒事,我們再舉個栗子:

var reg = /\w(?=\d)/g;
var text = 'a2*3';
var result = text.replace(reg,'X');
console.log(result);//X2*3
複製程式碼

需要注意,我們斷言裡面內容只是作為匹配的條件之一,也是必須的條件,但是匹配的本質只匹配"()"前面的正則,所以上面的結果為:'X2*3',而不是'X*3'。如果要匹配結果為後者,我們按原來的寫法就行了var reg = /\w\d/g;,不是嗎?

物件屬性

我們在用正規表示式相關的方法時,經常會用到正規表示式相關的一些物件屬性,下面我們總結一下正規表示式相關的物件屬性:

  • golbal: 是否全文搜尋,預設false
  • ignore case: 是否大小寫敏感,預設false
  • multiline: 多行搜尋,預設false
  • lastIndex: 是當前表示式匹配內容的最後一個字元的下一個位置
  • source: 正規表示式的文字字串

其中前面三個我們在上文中已經提到過了,source的話,我們一起結合起來看看程式碼:

var reg1 = /\w/;
var reg2 = /\w/gim;

console.log(reg1.global);//false
console.log(reg1.ignoreCase);//false
console.log(reg1.multiline);//false

console.log(reg2.global);//true
console.log(reg2.ignoreCase);//true
console.log(reg2.multiline);//true

console.log(reg1.source);//\w
console.log(reg2.source);//\w
複製程式碼

golbal、ignore case、multiline預設都是false,而source就是你寫的正則字串文字了。

至於lastIndex,我們先來看一個例子:

var reg1 = /\w/;
var reg2 = /\w/g;

console.log(reg1.test('a'));//true
console.log(reg1.test('a'));//true
console.log(reg1.test('a'));//true
//... 不管執行多少次都是true

console.log(reg2.test('ab'));//true
console.log(reg2.test('ab'));//true
console.log(reg2.test('ab'));//false
console.log(reg2.test('ab'));//true
console.log(reg2.test('ab'));//true
console.log(reg2.test('ab'));//false
//... 迴圈true true false
複製程式碼

對於上面的結果,是不是很奇怪?這就是'lastIndex'作用的結果了,(至於test方法,不懂的童鞋可以往下翻一番)。我們來輸出'lastIndex'試試:

var reg2 = /\w/g;
while(reg2.test('ab')){
  console.log(reg2.lastIndex); // 1   2
}
複製程式碼

可以發現,'lastIndex'是在不斷髮生變化的,即當前匹配結果的最後一個字元的下一個位置,這裡第一次匹配到'a'字元,匹配結果的最後一位字元也是'a','a'字元的下一個位置的index就是1了。類似的第二次匹配'b',匹配結果的最後一位字元也是'b','b'字元的下一個位置的index就是2。現在再看看概念,是不是明白了許多?

所以說,正則每次匹配並不是從頭開始的,而是從上次的結果往後找,看看後面還有沒有,有的話繼續匹配,當然這必須是在'g'全域性匹配的基礎上,不然每次匹配都是從頭開始的。

正規表示式RegExp物件本身的方法

RegExp物件自帶的方法總共有三個:

  1. test: 檢索字串中指定的值。返回 true 或 false
  2. exec: 檢索字串中指定的值。返回找到的值,並確定其位置
  3. compile: 編譯正規表示式(不常用)

test方法

test() 方法用於測試字串引數中是否存在匹配正規表示式模式的字串,如果存在則返回 true ,否則返回 false 。

語法為RegExpObject.test(string),如果字串 string 中含有與 RegExpObject 匹配的文字,則返回 true,否則返回 false。

舉個栗子:

var str = "good good study, day day up";
var reg = new RegExp("study");
var result = reg.test(str);
console.log(result);//true
複製程式碼

exec方法

exec() 方法用於使用正規表示式模式對字串執行搜尋,並將更新全域性 RegExp 物件的屬性以反映匹配結果。

語法為RegExpObject.exec(string),如果字串 string 中含有與 RegExpObject 匹配的文字,則返回一個陣列,其中存放匹配的結果。如果未找到匹配,則返回值為 null。

並且,陣列存在兩個額外的屬性:

  1. index: 宣告匹配文字的第一個字元的位置
  2. input: 存放被檢索的字串 String

exec()方法比較複雜,全域性呼叫和非全域性呼叫的結果不同:

非全域性(即不帶'g')呼叫

  • 呼叫非全域性的 RegExp 物件的exec()時,返回陣列
  • 陣列中第一個元素是正規表示式匹配的文字
  • 陣列中第二個元素是與 RegExpObject 的第一個子表示式相匹配的文字(如果有的話)
  • 陣列中第三個元素是與 RegExp 物件的第二個子表示式相匹配的文字(如果有的話,以此類推

看起來是不是又有點暈?不怕,我們來看看例子:

var reg3 = /\d(\w)\d/;
var str = '1a2b3c4d5e';
var arr = reg3.exec(str);
console.log(reg3.lastIndex + '\t' + arr.index + '\t' + arr.toString());//0	0	1a2,a
複製程式碼

即輸出結果為: lastIndex(這裡為0是因為非全域性匹配下lastIndex其實是不生效的),匹配文字的第一個字元的位置(額外屬性index,這裡第一個字元為'1')和匹配結果陣列('1a2'為匹配文字,'a'為子表示式'(\w)'的匹配結果,這裡不存在第二個子表示式'()',所以陣列裡面沒有第三個匹配結果的值)。是不是清楚了許多?

全域性呼叫

  • exec() 會在 RegExpObject 的 lastIndex 屬性指定的字元處開始檢索字串 string。
  • 當 exec() 找到了與表示式相匹配的文字時,在匹配後,它將把 RegExpObject 的 lastIndex 屬性設定為匹配文字的最後一個字元的下一個位置。
  • 這就是說,你可以通過反覆呼叫 exec() 方法來遍歷字串中的所有匹配文字。
  • 當 exec() 再也找不到匹配的文字時,它將返回 null,並把 lastIndex 屬性重置為 0。
  • 注意:如果在一個字串中完成了一次模式匹配之後要開始檢索新的字串,就必須手動地把 lastIndex 屬性重置為 0。

我們同樣來舉個栗子:

var reg4 = /\d(\w)(\w)\d/g;
var str = '$1az2bb3cy4dd5ee';

while(arr = reg4.exec(str)){
  console.log(reg4.lastIndex + '\t' + arr.index + '\t' + arr.toString());
  //5    1   1az2,a,z
  //11   7   3cy4,c,y
}
複製程式碼

這裡我就不在解釋結果的含義了,相信童鞋們看完上面的非全域性的解釋就可以理解這裡全域性的情況了。

compile方法

compile() 方法用於在指令碼執行過程中編譯正規表示式,也可用於改變和重新編譯正規表示式。這個我們在平時工作中一般不會用到(我至今沒看到過...);

它的語法為:RegExpObject.compile(regexp,modifier),其中引數'regexp'為正規表示式,引數'modifier'為規定匹配的型別。"g" 用於全域性匹配,"i" 用於區分大小寫,"gi" 用於全域性區分大小寫的匹配(摘錄於W3C)。

我們來看一個例子:

var str = "Every man in the world! Every woman on earth!";
var reg = /man/g;
var str2 = str.replace(reg,"person");
console.log(str2)

var reg2=/(wo)?man/g;
reg.compile(reg2);
console.log(reg.source);//(wo)?man  這裡可以看到reg通過compile編譯為reg2一樣了
var str2=str.replace(reg,"person");
console.log(str2);
複製程式碼

上面的意思就是:在字串中全域性搜尋 "man",並用 "person" 替換。然後通過 compile() 方法,改變正規表示式reg為reg2,並繼續利用正則改變後的正規表示式reg,用 "person" 替換 "man" 或 "woman"。

至於為什麼用compile動態改正則,那跟新建有啥區別呢?

我查了查資料是這麼說的:如果指定的正規表示式需要多次重複使用,那麼編譯正規表示式將會提高程式碼的執行效率,不過如果僅僅執行一次或者少數幾次,那麼將不會有明顯的效果,compile提高了正規表示式的適應性!

支援正規表示式的 String 物件的方法

支援正規表示式的 String 物件的方法有:

  • search: 檢索與正規表示式相匹配的值
  • match: 找到一個或多個正規表示式的匹配。
  • replace: 替換與正規表示式匹配的子串。
  • split: 把字串分割為字串陣列。

search

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

語法為stringObject.search(regexp),結果返回 stringObject 中第一個與 regexp 相匹配的子串的起始位置index,如果沒有找到任何匹配的子串,則返回 -1。

需要注意的是,search() 方法不執行全域性匹配,它將忽略修飾符'g',並且總是從字串的開始進行檢索。

來看一個例子:

var str = 'a1b2c3d4';
console.log(str.search('1')); //1
console.log(str.search('10')); //-1
console.log(str.search(/b2/)); //2
console.log(str.search(/\w\d/g)); //0
console.log(str.search(/\w\d/g)); //0 忽略'g',執行多次未返回不同結果
複製程式碼

match

match() 方法將檢索字串,以找到一個或多個與 RegExp 匹配的文字,在 RegExp 是否有修飾符'g'影響很大。該方法類似 indexOf() 和 lastIndexOf(),但是它返回指定的值,而不是字串的位置。

語法為stringObject.match(searchvalue)或stringObject.match(regexp),結果返回存放匹配結果的陣列。該陣列的內容依賴於 regexp 是否具有全域性標誌 g。

match() 方法也分全域性呼叫和非全域性呼叫:

非全域性呼叫

  • 如果 regexp 沒有標誌 g ,那麼 match() 方法就只能在字串中執行匹配一次
  • 如果沒有找到任何匹配文字,返回 null
  • 否則它將返回一個陣列,其中存放了與它找到的匹配文字有關的資訊:
  • 陣列的第 0 個元素存放的是匹配文字,而其餘的元素存放的是與正規表示式的子表示式匹配的文字。(這裡與前面說的exec()方法類似)

並且,陣列也存在兩個額外的屬性(與 exec() 方法基本相同):

  1. index: 宣告匹配文字的第一個字元的位置
  2. input: 宣告對 stringObject 的引用

下面是舉例:

var reg3 = /\d(\w)\d/;
var str = '1a2b3c4d5e';
var arr = str.match(reg3);
console.log(reg3.lastIndex + '\t' + arr.index + '\t' + arr.toString());//0  0  1a2,a
複製程式碼

可以看到結果都與 exec() 方法一樣,只是字串和正則的位置交換了一下。

全域性呼叫

全域性呼叫就和 exec() 不同了:

  • 如果 regexp 具有標誌 g 則 match() 方法將執行全域性檢索,找到字串中所有匹配的子字串
  • 沒有找到任何匹配的子字串則返回 null
  • 如果找到了一個或者多個匹配字串,則返回一個陣列
  • 陣列元素中存放的是字串中所有匹配的字串,而且也沒有index屬性和input屬性

簡單的說,就是返回一個陣列,陣列中放著所有匹配結果。

var reg4 = /\d(\w)(\w)\d/g;
var str = '$1az2bb3cy4dd5ee';
var arr = str.match(reg4)
console.log(arr); // ["1az2", "3cy4"]
console.log(reg4.lastIndex + '\t' + arr.index) //0	undefined
複製程式碼

可以看出,match() 方法功能沒有 exec() 方法返回那麼多各種資訊,但是如果只要結果陣列,match() 方法效率會高一些。

split

對於split()方法我就不詳細說明了,我們經常用它把字串分割為陣列。

var str = 'a,b,c,d';
var arr = str.split(',');
console.log(arr); //['a','b','c','d']
複製程式碼

但是你可能不知道,我們在一些複雜情況下我們可以使用正規表示式解決

var str = 'a1b2c3d';
var arr = str.split(/\d/);
console.log(arr); //['a','b','c','d']
複製程式碼

上面可能還看不出 spilt() 方法用正則分割的有點,但是如果複雜一點的分割呢,比如:

var str = 'a1b&c|d&e';
var arr = str.split(/[\d|&]/);
console.log(arr); //['a','b','c','d','e']
複製程式碼

這樣是不是看出了用正則的優勢呢?

小知識:其實,我們在用 split() 分割字元','的時候,split() 方法也是把',' 隱士轉換成正則'/,/'的, search() 方法和 replace() 方法也是一樣的。

replace

replace() 方法用於在字串中用一些字元替換另一些字元,或替換一個與正規表示式匹配的子串。

語法為stringObject.replace(regexp/substr,replacement),結果返回一個新的字串,是用 replacement 替換了 regexp 的第一次匹配或所有匹配之後得到的。

對於 replace() 方法,它有三種使用方式:

  1. String.prototype.replace(str,replaceStr);
  2. String.prototype.replace(reg,replaceStr);
  3. String.prototype.replace(reg,function);

1和2兩種的使用我就不再多舉例了,相信如果你認真看完前面的文章,肯定最熟悉的就是 replace() 方法了的一、二兩種用法了。 這裡就提一下第3種使用方法。

先說一下String.prototype.replace(reg,function);中 function 的引數含義,function 會在每次匹配替換的時候呼叫,有四個引數(第二個引數不固定):

  1. 匹配字串
  2. 正規表示式的分組內容,沒有分組則沒有該引數、
  3. 匹配項在字串中的 index
  4. 原字串

照例來舉兩個個栗子看看:

var str = 'a1b2c3d4e5';
var reg = /\d/g;
var arr = str.replace(reg,function(match, index, origin){
  console.log(index);// 1 3 5 7 9
  return parseInt(match) + 1;
})
console.log(arr);//a2b3c4d5e6 把每次匹配到的結果+1替換


var str = 'a1b2c3d4e5';
var reg = /(\d)(\w)(\d)/g;
var arr = str.replace(reg,function(match, group1, group2, group3, index, origin){
  console.log(match);// 1b2   3d4
  return group1 + group3;
})
console.log(arr);//a12c34e5  去除了每次匹配到的group2
複製程式碼

文章到這裡就全部結束了,每個例子都是實際測試輸出結果,碼了好久好久......希望文章對您有幫助,我就心滿意足了~~

相關文章