正規表示式之難點

lovecn發表於2019-07-30

本文轉自 coffeephp

斷言

?<= 這個是對需要匹配的目標左邊的(前面)的進行斷言,斷定它前面會出現的 但是不會被匹配到。如:

$subject = 'I am Lancer, Please say hello Lancer';

//目標: 我要把hello 後面的Lancer  改為  '!' .
$pattern = '/(?<=hello )Lancer/';
$result = preg_replace($pattern, '', $subject);
echo $result;  //I am Lancer, Please say hello !
這樣就成功咯~

?=,與上面的位置剛好相反,這個是對需要匹配的目標右邊的(後面)的進行斷言,斷定它後面會出現的 但是不會被匹配到。如:

$subject = 'I love you! I love her too!';

//目標:不能愛這麼多, 把第二個 'love' 改為 'hate'
$pattern = '/love(?= her)/';
$result = preg_replace($pattern, 'hate', $subject);
echo $result;  //'I love you! I hate her too!'

?<!這個是需要對匹配左邊的(前面的)進行斷言,不過它是非,找到不是這個的。還是拿第一個例子來說:

$subject = 'I am Lancer, Please say hello Lancer';

//目標: 我還是要把hello 後面的Lancer  改為  '!'  該怎麼做
$pattern = '/(?<!am )Lancer/';  //找到‘Lancer’前面不是'am '的'Lancer'
$result = preg_replace($pattern, '', $subject);
echo $result;  //I am Lancer, Please say hello !

?!還是一樣的秘方,還是一樣的味道~

$subject = 'I love you! I love her too!';

//目標:不能愛這麼多, 把第二個 'love' 改為 'hate'
$pattern = '/love(?! you)/';
$result = preg_replace($pattern, 'hate', $subject);
echo $result;  //'I love you! I hate her too!'

總結:這個斷言,作用主要在,對於很多同樣的目標,可是我只要其中的一個,或者多個的時候,那麼就可以根據它的前面和後面,進行斷言,來區分他們找到自己想要匹配的目標。

捕獲

先來說一下, 什麼叫捕獲。就是匹配之後,會根據你正規表示式中的()來進行分組。一一捕獲。打個比方:

//為了顯示方便,寫了個show函式
function show($str)
{
    if (empty($str)) {
        echo null;
    } elseif (is_array($str) || is_object($str)) {
        echo '<pre>';
        print_r($str);
        echo '</pre>';
    } else {
        echo $str;
    }
}
//--------------------------------------------------------------------
$subject  = '12323abcdea1233';
$pattern = '/(a)(b)(c)(d)(e)/';
preg_match_all($pattern, $subject, $matches);
show($matches);
//那麼你會覺得 $matches 會是什麼答案?
//你肯定知道是:
Array
(
    [0] => Array
        (
            [0] => abcde
        )

    [1] => Array
        (
            [0] => a
        )

    [2] => Array
        (
            [0] => b
        )

    [3] => Array
        (
            [0] => c
        )

    [4] => Array
        (
            [0] => d
        )

    [5] => Array
        (
            [0] => e
        )

)
//這個答案,大家應該都知道吧。索引為0的是整個match的內容,接著的
//就是捕獲的每一個()分組的內容。我們還可以這樣來寫:

$subject = '123abcabc123';
$pattern = '/(a)(b)(c)(\1)(\2)(\3)/';
preg_match_all($pattern, $subject, $matches);
show($matches);//??
先看答案:
Array
(
    [0] => Array
        (
            [0] => abcabc
        )

    [1] => Array
        (
            [0] => a
        )

    [2] => Array
        (
            [0] => b
        )

    [3] => Array
        (
            [0] => c
        )

    [4] => Array
        (
            [0] => a
        )

    [5] => Array
        (
            [0] => b
        )

    [6] => Array
        (
            [0] => c
        )

)
//你可能會有疑問, 咦,,, 怎麼(\1)和(a), (\2)和(b),(\3)和(c) 在正則裡是一樣的呢?
//其實 (a)就是指的第一組, 然後後面就可以用(\1)來表示。(b),(c)也一樣。

有人可能就會問了, 那你寫這個的作用又是什麼呢 ? 獲取這些括號裡的幹啥。。 我只要第一個索引的匹配就夠了呀。

但是, 你考慮到了替換這個因素沒? 如果我替換的時候需要()的東西呢? 這個時候,我們就可以用到捕獲到的()的東西來穿插。

不知道有人好奇過沒,為什麼用那些TP框架,Laravel框架, 或者smarty 在模版裡寫的{{$msg}}為什麼也能輸出呢? 其實就是用了正則替換~ 看程式碼:

$msg = "正則捕獲";
$subject = '<p>{{$msg}}</p>';
$pattern = '/\{\{(.*/)\}\}/';  //因為正則裡也有'{' 和'}'所以需要用‘\’轉義
$result = preg_replace($pattern, '<?php echo $1; ?>', $subject);
show($result); // <p><?php echo $msg;?></p> 
//成功修改~

上面說的是捕獲, 但是我可能不想捕獲怎麼辦? 那麼就可以用(?:) 在前面加上?:即可。注意, 這個不會影響匹配 只會影響捕獲。 如:

$subject = 'abc';
$pattern = '/(a)(?:b)(c)/';
preg_match_all($pattern, $subject, $matches);
show($matches);
//結果:
Array
(
    [0] => Array
        (
            [0] => abc
        )

    [1] => Array
        (
            [0] => a
        )

    [2] => Array
        (
            [0] => c
        )
)
//看 匹配的結果讓然是'abc' 不過沒有捕獲到 'b'
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章