正規表示式學習總結

eraser123發表於2017-03-20

一、背景

工作上遇到一個這樣的需求:

用正規表示式將一個字串中的span標籤替換為img標籤,並將原span標籤的內容放到img標籤的src中,問題詳細描述:點我

看到這個需求,我知道應該可以用正規表示式,可是由於之前沒怎麼用,一想到正規表示式就頭大,一堆各種各樣的特殊符號,似乎沒有規律可循,有點難以理解。不過知道自己不能逃避,於是自己就去嘗試怎麼寫這個正規表示式來解決我的需求,上述中提到的問題詳細描述,大概就是我思考的過程,問題提出後立馬有人解答,看完他們的答案後,慚愧,感覺到自己知識的欠缺,再不學習就老了(┬_┬)

二、正規表示式基礎

2.1 元字元介紹

  • “^”:^會匹配行或者字串的起始位置,有時還會匹配整個文件的起始位置。

  • “$”:$會匹配行或字串的結尾。

  • “b”:不會消耗任何字元只匹配一個位置,常用於匹配單詞邊界 如:我想從字串中”This is Regex”匹配單獨的單詞 “is” 正則就要寫成:"This is Regex".match(/is/); “b” 不會匹配is 兩邊的字元,但它會識別is 兩邊是否為單詞的邊界。

  • “d”:匹配數字。

  • “w”:匹配字母,數字,下劃線。等價於`[A-Za-z0-9_]`。

  • “s”:匹配空格。

  • “.”:匹配除了換行符以外的任何字元。

  • “[a-zA-Z]”:字元組 匹配包含括號內元素的字元。

  • 幾種反義:改成大寫,意思就與原來的相反。
    如:
    “W”:匹配任何非單詞字元。等價於`1`。
    2“:匹配除了abc以外的任意字元。

  • 字元轉義:在正規表示式中元字元是有特殊的含義的,當我們要匹配元字元本身時,就需要用到字元轉義,如:/./.test("."); // true

2.2 量詞

2.2.1 常用量詞

  • “*”(貪婪)重複零次或更多,貪婪量詞會首先匹配整個字串,嘗試匹配時,它會選定儘可能多的內容,如果 失敗則回退一個字元,然後再次嘗試回退的過程就叫做回溯,它會每次回退一個字元,直到找到匹配的內容或者沒有字元可以回退。如:
    "aaaaaa".match(/a*/) // ["aaaaaa"]

  • “?”(懶惰)重複零次或一次,懶惰量詞使用另一種方式匹配,它從目標的起始位置開始嘗試匹配,每次檢查一個字元,並尋找它要匹配的內容,如此迴圈直到字元結尾處。如:"aaaaaa".match(/a?/) // ["a"]

  • “+”(佔有)重複零次或更多次,佔有量詞會覆蓋事個目標字串,然後嘗試尋找匹配內容 ,但它只嘗試一次,不會回溯。如:
    "aaaaaa".match(/a+/) // ["aaaaaa"]

  • “{n}” 重複n次;如:
    "aaaaaa".match(/a{3}/) // ["aaa"]

  • “{n,m}” 重複n到m次;如:
    "aaaaaa".match(/a{3,4}/) // ["aaaa"]

  • “{n,}” 重複n次或更多次;如:
    "aaaaaa".match(/a{3,}/) // ["aaaaaa"]

2.2.1 懶惰限定符

  • “*?” 重複任意次,但儘可能少重複;如:"aabab".match(/a.*?b/) // ["aab"] 為什麼第一個匹配是aab(第一到第三個字元)而不是ab(第二到第三個字元)?簡單地說,因為正規表示式有另一條規則,比懶惰/貪婪規則的優先順序更高:最先開始的匹配擁有最高的優先權

  • “+?” 重複1次或更多次,但儘可能少重複,與上面一樣,只是至少要重複1次。如:"aabab".match(/a.+?b/) // ["aab"]

  • “??” 重複0次或1次,但儘可能少重複。如:"aabab".match(/a.??b/) // ["aab"]

  • “{n,m}?” 重複n到m次,但儘可能少重複。如:"aaa".match(/a{1,3}?/) // ["a"]

  • “{n,}?” 重複n次以上,但儘可能少重複。如:"aaa".match(/a{1,}?/) // ["a"]

2.2.2 處理選項

  • javascript中正規表示式支援的正規表示式有三個,g、i、m,分別代表全域性匹配、忽略大小寫、多行模式。三種屬性可以自由組合共存。

  • 在預設的模式下,元字元 ^ 和 $ 分別匹配字串的開頭和結尾處,模式 m 改變了這倆元字元的定義,讓他們匹配一行的開頭和結尾。

三、正則進階

3.1 捕獲分組

正規表示式一個最重要的特性就是將匹配成功的模式的某部分進行儲存供以後使用這一能力。對一個正規表示式模式或部分模式兩邊新增圓括號將導致這部分表示式儲存到一個臨時緩衝區中。(可以使用非捕獲元字元 `?:`, `?=`, 或 `?!` 來忽略對這部分正規表示式的儲存。)

所捕獲的每個子匹配都按照在正規表示式模式中從左至右所遇到的內容儲存。儲存子匹配的緩衝區編號從 1 開始,連續編號直至最大 99 個子表示式。每個緩衝區都可以使用 `n` 訪問,其中 n 為一個標識特定緩衝區的一位或兩位十進位制數。

後向引用一個最簡單,最有用的應用是提供了確定文字中連續出現兩個相同單詞的位置的能力。舉個例子:

/([a-zA-Z]+)s+1/.exec(" asd sf  hello hello asd"); //["hello hello", "hello"]

解釋這個例子:

1、(b[a-zA-Z]+b) 是一個捕獲分組,它捕獲所有的單詞,

" asd sf  hello hello asd".match(/([a-zA-Z]+)/g) // ["asd", "sf", "hello", "hello", "asd"]

注:加上/g這個處理選項是便於我理解,沒有這個選項的時候,只輸出第一個單詞asd。
2、s加了一個空格限制條件,所以最後一個單詞被排除,

" asd sf  hello hello asd".match(/([a-zA-Z]+)s/g) \ ["asd ", "sf ", "hello ", "hello "]

3、”1″後向引用,

" asd sf  hello hello asd".match(/([a-zA-Z]+)s+1/g) \ ["hello hello"]

說實話,這個例子花了我很長時間去理解,有一點點想通,感覺這個概念看起來容易,寫起來並不容易啊。

3.2 捕獲分組常有的用法(斷言)

  • “(exp)” 匹配exp,並捕獲文字到自動命名的組裡;如:

/(hello)sworld/.exec("asdadasd hello world asdasd") // ["hello world", "hello"]
  • “(?:exp)” 匹配exp,不捕獲匹配的文字,也不給此分組分配組號;如:

/(?:hello)sworld/.exec("asdadasd hello world asdasd")  // ["hello world"]
  • “(?=exp)” 用來捕獲exp前面的字元,分組中的內容不會被捕獲,也不分配組號;如:

/hellos(?=world)/.exec("asdadasd hello world asdasd")  // ["hello "]
  • “(?!exp)” 捕獲後面不是exp的字元,同樣不捕獲分組的內容,也不分配組號;如:

/hellos(?!world)/.exec("asdadasd hello world asdasd") //null
 world改變一下:
/hellos(?!world)/.exec("asdadasd hello wosrlds asdasd") //["hello "]
  • “(?<!exp)” 匹配前面不是exp的位置;如:

/(?!<d)123/.exec("abc123 ") // ["123"]

四、Javascript中正規表示式的使用

在JavaScript中定義一個正規表示式語法為:

var reg=/hello/    或者  var reg=new RegExp("hello")

接著列舉一下JavaScript中可以使用正規表示式的函式,並簡單介紹一下這些函式的作用。

4.1 String.prototype.search方法

用來找出原字串中某個子字串首次出現的索引index,沒有則返回-1。可以在官方文件中瞭解更多。

"abchello".search(/hello/);  //  3

4.2 String.prototype.replace方法

用來替換字串中的子串。簡單例子:

"abchello".replace(/hello/,"hi");   //  "abchi"

官方文件中有提到:

如果第一個引數是 RegExp物件,那麼替換字串可以插入特殊變數名$n,n是個小於100的非負整數,表示插入第 n 個括號匹配的字串。

所以我在文中一開始提到的需求就可以用
str.replace(/<span>(.*?)</span>/g, `<img src="$1"/>`) [$1表示/<span>(.?)</span>/g中的“(.?)”)匹配的字串]
答案來解答。

4.3 String.prototype.split方法

用來分割字串

"abchelloasdasdhelloasd".split(/hello/);  //["abc", "asdasd", "asd"]

4.4 String.prototype.match方法

用來捕獲字串中的子字串到一個陣列中。預設情況下只捕獲一個結果到陣列中,正規表示式有”全域性捕獲“的屬性時(定義正規表示式的時候新增引數g),會捕獲所有結果到陣列中。

"abchelloasdasdhelloasd".match(/hello/);  //["hello"]
"abchelloasdasdhelloasd".match(/hello/g);  //["hello","hello"]

4.5 RegExp.prototype.exec方法

和字串的match方法類似,這個方法也是從字串中捕獲滿足條件的字串到陣列中,但是也有兩個區別。
1、exec方法一次只能捕獲一份子字串到陣列中,無論正規表示式是否有全域性屬性

/hello/g.exec("abchelloasdasdhelloasd"); // ["hello"]

2、正規表示式物件(也就是JavaScript中的RegExp物件)有一個lastIndex屬性,用來表示下一次從哪個位置開始捕獲,每一次執行exec方法後,lastIndex就會往後推,直到找不到匹配的字元返回null,然後又從頭開始捕獲。 這個屬性可以用來遍歷捕獲字串中的子串。

var reg=/hello/g;
reg.lastIndex; //0
reg.exec("abchelloasdasdhelloasd"); // ["hello"]
reg.lastIndex; //8
reg.exec("abchelloasdasdhelloasd"); // ["hello"]
reg.lastIndex; //19
reg.exec("abchelloasdasdhelloasd"); // null
reg.lastIndex; //0

4.6 RegExp.prototype.test方法

用來測試字串中是否含有子字串

/hello/.test("abchello");  // true

五、總結

總算是對正規表示式瞭解了一些,要熟練掌握還需後面多多實踐^_^
參考資料:
1.http://www.cnblogs.com/zery/p…
2.http://www.cnblogs.com/tzyy/p…
3.http://www.codeyyy.com/regex/…


  1. A-Za-z0-9_
  2. abc

相關文章