对正则表达式总是望而生畏,现趁有空,补补这块的知识,记录一下正向预查与反向预查:


1.正向预查

现在,我们假设需要仅匹配 Windows,不匹配后面的版本号,并且要求 Windows 后面的版本号只能是 数字类型,换言之,XP 和 Vista 不能被匹配,
在正则表达式中,可以使用 正向预查 来解决这个问题。本例中,写法是:“Windows(?= [\d.]+\b)”。
它的语法是在 子模式内部 前面加“?=”,表示的意思是:首先,要匹配的文本必须满足此子模式前面的表达式(本例,“Windows ”);其次,此子模式不参与匹配。

Text:
Windows 1.03 and Windows 2.0 fisrt Released in 1985 and 1987 respectively.
Windows 95 and Windows 98 are the successor.
Then Windows 2000 and Windows Xp appeared.
Windows Vista is the Latest version of the family.

RegEx:
Windows( ?=[\d.]+\b)


Result:(带下划线的为成功匹配的)
Windows 1.03 and Windows 2.0 fisrt Released in 1985 and 1987 respectively.
Windows 95 and Windows 98 are the successor.
Then Windows 2000 and Windows Xp appeared.
Windows Vista is the Latest version of the family.

PHP代码如下:

<?php


$reg = "#Windows (?=[\d.]+\b)#";
$str = "Windows 1.03 and Windows 2.0 fisrt Released in 1985 and 1987 respectively.
        Windows 95 and Windows 98 are the successor.
        Then Windows 2000 and Windows Xp appeared.
        Windows Vista is the Latest version of the family.";


if ( preg_match_all($reg, $str, $match) ) {
    // code...
    var_dump($match);
} else {
    // code...
    echo("error code");
}

结果:
array(1) {
  [0]=>
  array(5) {
    [0]=>
    string(8) "Windows "
    [1]=>
    string(8) "Windows "
    [2]=>
    string(8) "Windows "
    [3]=>
    string(8) "Windows "
    [4]=>
    string(8) "Windows "
  }
}


可以将 正向预查 理解成为自定义的边界(\b),这个边界位于表达式末。
反言之,你可以将位于表达式末的 \b 理解成非获取匹配的一个特例:(?=[ ,.\r\n<>;\-])。注意,这里没有写全边界符号。
Text:
aaaax-aaa 

aaaaxaaaa


RegEx:
x(?=[,.\r\n<>;\-])


Result:
aaaax-aaa
aaaaxaaaa

你也可以这样理解上面的匹配过程:
1. 先进行普通匹配:Windows ([\d.]+\b)
2. 然后从匹配文本中将 子模式 内的文本排除掉。


2.反向预查

在上面的例子中,我们知道 正向预查 类似于自定义的 位于文本末 的字符边界。那么自然应该有位于文本首的情况,比如说,我们要匹配下面文本中属于 CNY 的金额:

Text:
CNY: 128.04
USD: 22.5
USD: 23.5
HKD: 1533.5
CNY: 23.78

RegEx:
CNY: \d+\.\d+

Result:
CNY: 128.04 

USD: 22.5 

USD: 23.5 

HKD: 1533.5 

CNY: 23.78


与上面类似,我们现在要求仅匹配金额,而不匹配前面的 “CNY:”
正则表达式中,可以使用 反向预查 来解决这个问题。本例中,写法是:(?<=CNY: )\d+\.\d+
反向预查 的语法是在子模式内部前面加“?<=”,表示的意思是:首先,要匹配的文本必须满足此子模式 后面 的表达式(本例,“\d+.\d+”);其次,此子模式不参与匹配。
Text:

<?php


$reg = "#(?<=CNY: )\d+\.\d+#";
$str = "CNY: 128.04
        USD: 22.5
        USD: 23.5
        HKD: 1533.5
        CNY: 23.78";


if ( preg_match_all($reg, $str, $match) ) {
    // code...
    var_dump($match);
} else {
    // code...
    echo("error code");
}

结果:
array(1) {
  [0]=>
  array(2) {
    [0]=>
    string(6) "128.04"
    [1]=>
    string(5) "23.78"
  }
}


与前面类似:你可以将 反向预查 理解成为自定义的边界(\b),这个边界位于 表达式首。
反言之,你可以将位于 表达式首 的 \b 理解成一个非获取匹配的一个特例:(?<=[ ,.\r\n<>;\-])。注意,我没有写全所有边界。
Text:
aaa-xaaaa
aaaxaaaaa
RegEx:
(?<=[,.\r\n<>;\-])x
Result:
aaa-xaaaa
aaaxaaaaa

你也可以这样理解上面的匹配过程:
1. 先进行普通匹配:(CNY: )\d+\.\d+
2. 然后从匹配文本中将 子模式 内的文本排除掉。