Stories

Detail Return Return

javascript 正則 高級用法 - Stories Detail

先簡單看幾個常用基礎標識符

^ 匹配一個輸入或一行的開頭,

  /^a/
  // 匹配"an A",而不匹配"An a"

$ 匹配一個輸入或一行的結尾

/a$/
// 匹配"An a",而不匹配"an A"

*匹配前面元字符0次或多次

/ba*/
// 匹配b,ba,baa,baaa,...

+匹配前面元字符1次或多次

/ba+/
// 匹配ba,baa,baaa,...

? 匹配前面元字符0次或1次

/ba?/
// 匹配b,ba
(x) //匹配x保存x在名為$1...$9的變量中
x|y //匹配x或y
{n} //精確匹配n次
{n,} //匹配n次以上
{n,m} //匹配n-m次
[xyz] //字符集,匹配這個集合中的任一一個字符(或元字符),匹配x,y,z
[^xyz] //不匹配這個集合中的任何一個字符

正則表達式(Regular Expression)其實是一門工具,通過字符串模式匹配,實現搜索和替換功能。

它起源於20世紀50年代科學家在數學領域做的一些研究工作,後來才被引入到計算機領域中。

從它的命名我們可以知道,它是一種用來描述規則的表達式。而它的底層原理也十分簡單,就是使用狀態機的思想進行模式匹配。

這裏先不細糾概念,常用的方法屬性,文檔mdn什麼都有,也不糾結,直奔主題。

子表達式

子表達式(Reg)具有獨立的匹配功能,保存獨立的匹配結果

  1. 作為獨立單元可以使用*+?{n,m}等量詞

    /(ab)?c)/ 
    // 匹配c或者abc
  2. 作為子模式可以獨立處理,並且保留匹配結果子串,可通過RegExp.$1,...$n訪問

    var re = /(\w+)\s(\w+)/;
    var str = "John Smith";
    var newstr = str.replace(re, "$2, $1");
    console.log(newstr); // Smith, John

    以下RegExp屬性,不是w3c標準,不過大部瀏覽器支持

    RegExp屬性 描述
    $n 第 n 個子表達式匹配的字符串,只有1-9
    $& 最後匹配到的字符串,RegExp.lastMatch別名
    $' 最新匹配的右側子串,RegExp.rightContext 別名
    $` 最新匹配的左側子串,RegExp.leftContext別名
    $+ 匹配到的最後一個子串,RegExp.lastParen別名
    $_ 被匹配成功的原字符串,RegExp.input別名

    對應replace函數中訪問正則結果的參數

    變量名 描述
    $n 插入第 n 個子表達式匹配的字符串,只有1-99
    $& 插入匹配的子串
    $' 插入當前匹配的子串右邊的內容
    $` 插入當前匹配的子串左邊的內容
    $<name> 匹配[Name]具名子表達式的字符串
  3. 回溯引用(反向引用),模式的後面部分引用前面子表達式已經匹配到的子字符串
    通過反斜槓\加數字來實現的。數字代表子表達式在該正則表達式中的順序。例如: \1 引用的是第一個子表達式的匹配結果

    匹配結果不是匹配模式
    var s = "<h1>title<h1><p>text<p>";
    var r = /(<\/?\w+>).*\1/g;
    // 相當於/(<\/?\w+>).*(<h1>|<p>)/g
    // 再加(<\/?\w+>)匹配結果 === (<h1>|<p>)匹配結果
    var a = s.match(r);  //返回數組["<h1>title<h1>","<p>text<p>"]

子表達式的高級模式

非捕獲模式,匹配結果不會保留
/(?:\w+)\s(\w+)/
// ?:標識非捕獲
// $1 從第二個子表達式開始計算
命名捕獲,這個用的較少
var re = /(?<myName>\w+)\s(\w+)/;
console.log("John Smith".match(re));
// (?<Name>x)
// 匹配結果保存在匹配項的 groups 屬性中

1672889918820.png

斷言

斷言用來限制正則匹配的邊界。

其實^,&,\b,\B也是斷言。

  • ^ 對應字符串開頭
  • & 字符串結尾
  • \b 單詞邊界
  • \B 非單詞邊界

這裏主要説其他四種斷言

  1. 先行斷言,/x(?=y)/ y在後面跟隨x的時候匹配x
  2. 先行否定斷言,/x(?!y)/ y沒有在後面跟隨x的時候匹配x
  3. 後行斷言,/(?<=y)x/ y在x前面緊隨的時候匹配x
  4. 後行否定斷言,/(?<!y)x/ y沒有在x前面緊隨的時候匹配x

    // 先行斷言
    let regex = /First(?= test)/g;
    console.log('First test'.match(regex)); // [ 'First' ]
    
    // 先行否定斷言
    // /\d+(?!\.)/ 匹配沒有被小數點跟隨且至少有一位的數字。 /\d+(?!\.)/.exec('3.141') 匹配 "141" 而不是 "3"
    console.log(/\d+(?!\.)/g.exec('3.141')); // [ '141', index: 2, input: '3.141' ]
    
    // abc後面不能跟隨de
    let reg = /abc(?!de)/;
    reg.test('abcdefg');  // false;
    reg.test('abcd');  // true;
    reg.test('abcabc');   // true;

注意:匹配結果是不包括y的

正則表達式的三種模式

在使用修飾匹配次數的特殊符號時,有幾種表示方法可以使同一個表達式能夠匹配不同的次數,比如:"{m,n}", "{m,}", "?", "*", "+",具體匹配的次數隨被匹配的字符串而定。

  1. 貪婪模式
    貪婪模式總是儘可能多的匹配

    var regex = /\d{2,5}/g;
    var string = "123 1234 12345 123456";
    console.log( string.match(regex) );
    // => ["123", "1234", "12345", "12345"]
  2. 懶惰模式
    在修飾匹配次數的特殊符號後再加上一個 "?" 號,則可以使匹配次數不定的表達式儘可能少的匹配,使可匹配可不匹配的表達式,儘可能的 "不匹配"

    var regex = /\d{2,5}?/g;
    var string = "123 1234 12345 123456";
    console.log( string.match(regex) );
    // => ["12", "12", "34", "12", "34", "12", "34", "56"]
    其中 /\d{2,5}?/ 表示,雖然 2 到 5 次都行,當 2 個就夠的時候,就不再往下匹配
  3. 獨佔模式(js不支持)
    如果在表達式後加上一個加號(+),則會開啓獨佔模式。同貪婪模式一樣,獨佔模式一樣會匹配最長。不過在獨佔模式下,正則表達式儘可能長地去匹配字符串,一旦匹配不成功就會結束匹配而不會回溯。

正則的性能

正則引擎主要的兩大類:一種是DFA(確定型有窮自動機),另一種是NFA(不確定型有窮自動機)。NFA 對應正則表達式主導的匹配,DFA 對應文本主導的匹配。

DFA從匹配文本入手,從左到右,每個字符不會匹配兩次,它的時間複雜度是多項式的,所以通常情況下,它的速度更快,但支持的特性很少,不支持捕獲組、各種引用等等;

NFA則是從正則表達式入手,不斷讀入字符,嘗試是否匹配當前正則,不匹配則吐出字符重新嘗試,通常它的速度比較慢,最優時間複雜度為多項式,最差情況為指數級

NFA支持更多的特性,因而絕大多數編程場景下(包括java,js),我們面對的是NFA。

吐出字符重新嘗試就是回溯

正則表達式回溯法原理

user avatar toopoo Avatar linlinma Avatar hyfhao Avatar 54r9rxzy Avatar wubomu Avatar tonyyoung Avatar tinygeeker Avatar minnanitkong Avatar jackn Avatar windseek Avatar columsys Avatar lewyon Avatar
Favorites 49 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.