[实践OK]PHP正则匹配反斜杠''和美元'$'的方法,和Ansible里的替换$有异曲同工的机理之处理,结合正则猫RegexBuddy的正确用法。 不指定

jackxiang 2018-6-27 11:23 | |
基础知识:
对于斜杠来讲,在PHP里即使是单引号,它也是会和类似双引号里的$一样,有被转义:
'/\\\\/'
cat a.php

php a.php
\
cat reg3.php  


来自微信群:是因为双引号的原因,\#在双引号里,就是\#,\\#在双引号里,就是\#,\\\#在双引号里,就是\\#。

之前http://jackxiang.com/post/6466/研究过反斜杠,没有研究过$,这次主要研究$。
"^-? \\d+$":这个正则表达式为什么会有两个反斜杠
这要分两步看
首先字符串中的\\被编译器解释为\
然后作为正则表达式,\d又被正则表达式引擎解释为元字符只匹配数字
正则表达式中匹配一个反斜杠要用四个反斜杠,为什么呢?

分析一下“\\\\”,第一个斜杠是转义符,第二个斜杠是斜杠本身,第三个斜杠是转义符,第四个斜杠是斜杠本身。
有2点要清楚:
1.字符串里面表示斜杠就需要两个斜杠如“\\”
2.正则表达式里的斜杠需要转意,是用“\\”标示。
这样就比较好解释:
我们先要表示正则表达式里面的斜杠“\\”,然后再用字符串表示出来。而这2个斜杠分别需要一个转义符,这样就成了4个斜杠在正则表达式里面表示一个斜杠。
From:https://my.oschina.net/airship/blog/411045

php实践如下:



背景:在Ansible里是Python正则替换时遇到 $符号时会有三个斜杠(http://jackxiang.com/post/8857/),而为什么是三个斜杠呢?

最外层-a""双引,不看regexp的单引号。
reg.php


a.txt
123$ab


运行:php reg.php
会提示:PHP Notice:  Undefined variable: ab in /data/codesdev/testdemo/php/reg/reg.php on line 2,
这里的PHP用双引号里面的$会被PHP的解释器解释为一个变量的,如这儿便被认为$ab是一个变量,
用单引号就不存在这个问题,如果非要用单引号,就得转义那个\$,也就让PHP解释器认为这个串里的$仅仅是一个$符号。

三个斜杠的原因,第一个斜杠就是前面讲的双引号里面的$转义为非变量的$,仅仅是一个字符的$,外层双引号为最大,只要外层是引号,在里面加单引也没有用:
$ echo $a
123
$ echo "$a"
123
$ echo "\$a"
$a
$ echo "'\$a'"
'$a'
第二个斜杠是放在正则双引号里面的,因为这个$符号在正则里面看它在正则里的意思是行尾,我们需要认为它在正则里面还是一个$符号,不表示行尾,加第二个斜杠转义表示在正则里的$符号(preg_match"里面是正则的眼光看,这里以#号为正则范围),而这第二个斜杠它在双下号里又认为它是转义的一个斜杠,而不是真正的斜杠,要正的斜杠则要再加一个斜杠,于是有了第三个斜杠。



cat reg2.php


cat a2.txt
123\$abcdefg

#php reg2.php
int(4)
int(1)
array(1) {
  [0]=>
  string(1) "\"
}


RegexBuddy的使用方法和来自:https://www.jb51.net/article/104895.htm的一个对照,如下:
test.php


正则猫RegexBuddy,Match-->
选个Python吧,能匹配的正则表达式:
<td>[0-9]{7,}<\\/td>\d\$$

Test:
1111111<td>2222222<\/td>3$

而上文里多的斜杠,从哪儿得出来呢?
Copy->CopyRegex as...->CopyRegex as PHP string
'<td>[0-9]{7,}<\\\\/td>\d\$$'
Copy->CopyRegex as...->CopyRegex as PHP '//' preg String
'%<td>[0-9]{7,}<\\\\/td>\d\$$%' ,替换为最左最右斜杠:
'/<td>([0-9]{7,})<\\\\/td>\d\$$/'   #正则猫的正则拷贝Copy结果,加上数字匹配的括号
'/<td>([0-9]{7,})<\\\\\/td>\d\\$/'  #PHP能成功运行并找到正确匹配结果的字符串正则,上面四个斜杠多两个斜杠,\d后面多一个斜杠。

也就是说放到PHP里面还得对斜杠再次转义,让正则认为它是真正的斜杠,而不是PHP里的转义符。

为何四个斜杠加两个斜杠?正则里的反斜杠转义为正则里的字符,但引号里的反斜杠还得转为PHP的正常认为的一个反斜杠,所以得加两个斜杠:
摘自:https://blog.csdn.net/xiaoxiaoniaoer1/article/details/7717669
   "\\"==> 反斜杠

在双引号内使用这些字符时,它们具有特殊的含义
转义字符代码  转义字符的含义
\ "  双引号
\ '  单引号
\ \  反斜杠
\ n  换行符
\ r  回车符
\ t  制表符
\ $  美元符号


实践发现放正则里的内容于外面是双引号单引号无关,只是双引号要转义,只要内容一样,匹配也一样的:
                                            

php reg.php
int(3)
int(1)
array(1) {
  [0]=>
  string(2) "$a"
}

然而,如果preg_match里的正则用单引号,那就匹配不出来了,如下:


php reg.php
int(3)
int(0)
array(0) {
}


cat reg.php  preg_match里的正则用单引号时,少一个斜杠就能匹配出来:
<?php
//$str = "123\$ab";
$str = '123$ab';
//$str = file_get_contents('a.txt');
var_dump(strpos($str,"\$a"));
var_dump(preg_match('#\\\$a#',$str,$rs));
var_dump($rs);

php reg.php  
int(3)
int(1)
array(1) {
  [0]=>
  string(2) "$a"
}


反过来说明啥?说明正则猫RegexBuddy默认是单引进行PHP的匹配,所以前面不用再加两个斜杠转义,证明下:

RegexBuddy采用双引号,也就无法匹配了:
php test.php
not match

得证:
$pattern = "%<td>([0-9]{7,})<\\\\/td>\d\$$%";      //not match
$pattern = "%<td>([0-9]{7,})<\\\\\\/td>\d\\$$%";  //matched
$pattern = '%<td>([0-9]{7,})<\\\\/td>\d\$$%';       //matched
也就是说RegexBuddy已经指明用单引号了,再回头看前面的:
Copy->CopyRegex as...->CopyRegex as PHP '//' preg String        <----它本来就是一个单引号'//' ,指明了,自己瞎搞要用双引号,双引号就再加一个对斜杠的转义即可!!!

回到背景里的这个Ansible,Ansible是用Python写的本质上是一样的道理:

                     '"referer":"$http_referer",'
                     '"agent":"$http_user_agent",'
                     '"http_cdn_src_ip":"$http_cdn_src_ip",'
+                    '"http_cdn_src_ip":"$http_cdn_src_ip",'
                     '"status":"$status"}';

     access_log  /data/logs/nginx/access.log main;

10.71.11.39 | SUCCESS => {
    "backup": "",
    "changed": true,
    "msg": "line replaced"
}

而前面用的是双引号,那么改成单引号怎么修改呢?和上面这个还不一样,它是写入到Client端后再运行的,有点不一样,用正则猫试试:
Match:
((.*)(\"agent\":\"\$http_user_agent\")(.*))
Test:
'"agent":"$http_user_agent",'
拷贝出来:
'/((.*)(\"agent\":\"\$http_user_agent\")(.*))/'




之前那个:
Match:
<td>([0-9]{7,})<\\/td>\d\$$
Test:
1111111<td>2222222<\/td>3$
拷贝出来:
'%<td>([0-9]{7,})<\\\\/td>\d\$$%'

发现没,那个斜杠是多到四个,但是后面的$并没有加斜杠,说明这只猫并不聪明,还得靠人人为把:
\$整成
\\$

而PHP里对这个 $前的斜杠并不敏感:
$pattern = '%<td>([0-9]{7,})<\\\\/td>\d\\$$%';
$pattern = '%<td>([0-9]{7,})<\\\\/td>\d\\$$%';
都能匹配出来:
#php test.php
Array
(
    [0] => Array
        (
            [0] => <td>2222222<\/td>3$
        )

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

)


总之,不能全信正则猫,实践得出单引号会少用斜杠,双引号就要多用斜杠。:




EOF

作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:http://jackxiang.com/post/9805/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!


最后编辑: jackxiang 编辑于2018-6-28 13:44
评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]