🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## **正向零宽断言** 用于**查找在某些内容(但并不包括这些内容)之前或之后的东西**,也就是说它们像\\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。最好还是拿例子来说明吧: (?=exp)也叫**零宽度正预测先行断言**,它断言自身出现的位置的后面能匹配表达式exp。比如\\b\\w+(?=ing\\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.时,它会匹配sing和danc。 (?<=exp)也叫**零宽度正回顾后发断言**,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\\bre)\\w+\\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。 假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:((?<=\\D)\\D{3})+\\b,用它对xxxxxxxxxx进行查找时结果是xxxxxxxxx 下面这个例子同时使用了这两种断言:(?<=\\s)\\d+(?=\\s)匹配以空白符间隔的数字(再次强调,不包括这些空白符) 断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。 ## **负向零宽断言** 如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样: \\b\\w\*q\[^u\]\\w\*\\b匹配包含后面不是字母u的字母q的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为\[^u\]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的\[^u\]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\\w\*\\b将会匹配下一个单词,于是\\b\\w\*q\[^u\]\\w\*\\b就能匹配整个Iraq fighting。负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。,我们可以这样来解决这个问题:\\b\\w\*q(?!u)\\w\*\\b。 零宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp。例如:\\d{3}(?!\\d)匹配三位数字,而且这三位数字的后面不能是数字;\\b((?!abc)\\w)+\\b匹配不包含连续字符串abc的单词。 同理,我们可以用(?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:(?<!\[a-z\])\\d{7}匹配前面不是小写字母的七位数字。 请详细分析表达式(?).\*(?=),这个表达式最能表现零宽断言的真正用途。 一个更复杂的例子:(?).\*(?=)匹配不包含属性的简单HTML标签内里的内容。(?)指定了这样的前缀:被尖括号括起来的单词(比如可能是),然后是.\*(任意的字符串),最后是一个后缀(?=)。注意后缀里的\\/,它用到了前面提过的字符转义,将”/“转义;\\1则是一个反向引用,引用的正是捕获的第一组,前面的(\\w+)匹配的内容,这样如果前缀实际上是的话,后缀就是了。整个表达式匹配的是和之间的内容(再次提醒,不包括前缀和后缀本身)。 (?:)(?=)(?!)(?<=)(?<!)(?i)  零宽断言 可简单理解断言为条件 >[danger]注意断言的pattern条件必须是固定的如例子所给,而不是(^>)*这种 <table class="table-view log-set-param"> <tbody> <tr> <td align="left" valign="middle" width="75"> <div class="para">?</div> </td> <td align="left" valign="middle" width="658"> <div class="para">当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+”将尽可能多的匹配“o”,得到结果[“oooo”],而“o+?”将尽可能少的匹配“o”,得到结果 ['o', 'o', 'o', 'o']</div> </td> </tr> <tr> <td align="left" valign="middle" width="75"> <div class="para">.点</div> </td> <td align="left" valign="middle" width="658"> <div class="para">匹配除“\r\n”之外的任何单个字符。要匹配包括“\r\n”在内的任何字符,请使用像“[\s\S]”的模式。</div> </td> </tr> <tr> <td align="left" valign="middle" width="75"> <div class="para">(pattern)</div> </td> <td align="left" valign="middle" width="658"> <div class="para">匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“\(”或“\)”。</div> </td> </tr> <tr> <td align="left" valign="middle" width="75"> <div class="para">(?:pattern)</div> </td> <td align="left" valign="middle" width="658"> <div class="para">获取匹配,匹配pattern但不获取匹配结果,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分时很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。<code> $subject="industry and industries !"; $pattern="/industr(?:y|ies)/"; $a=preg_match_all($pattern, $subject, $matches, PREG_OFFSET_CAPTURE); dump($matches); 结果: array:1 [▼ 0 => array:2 [▼ 0 => array:2 [▼ 0 => "industry" 1 => 0 ] 1 => array:2 [▼ 0 => "industries" 1 => 13 ] ] ] </code> </div> </td> </tr> <tr> <td align="left" valign="middle" width="75"> <div class="para"><span style="color: #ff6600;"><span style="color: #808080;"><span style="background-color: #ffffff; color: #808000;">find</span>(</span>?=pattern)</span></div> </td> <td align="left" valign="middle" width="658"> <div class="para">(<strong>零宽度正预测先行断言</strong>)<span style="color: #ff6600;">正向预搜索(匹配find右边是pattern的find 即它断言此位置的后面能匹配表达式pattern)</span>非获取匹配,正向肯定预查,该匹配不需要获取供以后使用。例如判断Windows右侧是否出现pattern出现则匹配,“<span style="color: #0000ff;">Windows(?=95|98|NT|2000)</span>”能匹配“Windows95、Windows98、Windows2000、WindowsNT”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。<br> <code> $str=" fgets() fgetss([\\\$allowable_tags ] )"; $pattern="/[\w]+(?=[(])/"; preg_match_all($pattern,$str ,$matches); </code> </div> </td> </tr> <tr> <td align="left" valign="middle" width="75"> <div class="para"><span style="color: #ff6600;"><span style="color: #808000;">find</span>(?!pattern)</span></div> </td> <td align="left" valign="middle" width="658"> <div class="para"> <p>(<strong>零宽度负预测先行断言</strong>)<span style="color: #ff6600;">正向预搜索(匹配find右边不是pattern的find)</span>非获取匹配,正向否定预查,该匹配不需要获取供以后使用。例如“<span style="color: #0000ff;">Windows(?!95|98|NT|2000)</span>”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。</p> <pre class="best-text mb-10">\b((?!abc)\w)+\b:匹配由字母或数字或下划线或汉字组成的字串,但字串中不能出现abc</pre> </div> </td> </tr> <tr> <td align="left" valign="middle" width="75"> <div class="para"><span style="color: #ff6600;">(?&lt;=pattern)<span style="color: #808000;">find</span></span></div> </td> <td align="left" valign="middle" width="658"> <div class="para">(<strong>零宽度正回顾后发断言</strong>)<span style="color: #ff6600;">反向预搜索(匹配find左边是pattern的find)</span> 匹配pattern后面的位置&nbsp;&nbsp;非获取匹配,反向肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?&lt;=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。 例子:取出【】里的内容 <code> $strSubject = "abc【111】abc【222】abc【333】abc"; $strPattern = "/(?<=【)[^】]+/"; $arrMatches = []; preg_match_all($strPattern, $strSubject, $arrMatches); var_dump($arrMatches); </code> </div> </td> </tr> <tr> <td align="left" valign="middle" width="75"> <div class="para"><span style="color: #ff6600;">(?&lt;!pattern)<span style="color: #808000;">find</span></span></div> </td> <td align="left" valign="middle" width="658"> <div class="para">(<strong>零宽度负回顾后发断言</strong>)&nbsp;<span style="color: #ff6600;">反向预搜索(匹配find左边不是pattern的find)</span>&nbsp;匹配前面不是pattern的位置&nbsp; 非获取匹配,反向否定预查,与正向否定预查类似,只是方向相反。例如“(?&lt;!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。这个地方不正确,有问题</div> <div class="para">此处用或任意一项都不能超过2位,如“(?&lt;!95|98|NT|20)Windows正确,“(?&lt;!95|980|NT|20)Windows 报错,若是单独使用则无限制,如(?&lt;!2000)Windows 正确匹配</div> </td> </tr> </tbody> </table> ## 环视 (?=pattern)与(?!pattern)属于一类,在正则表达式中叫**环视**。 “环视”这个词从字面理解就是确定“周围环境”。 环视一共有四种:(?=pattern)、(?!pattern)、(?<=pattern)、(?<!pattern)。 前两种是顺序环视(lookahead),也叫做前瞻 (?=pattern)、(?!pattern) 后两种叫逆序环视(look-behind),也叫后顾 (?<=pattern)、(?<!pattern) 每种又有“=”和“!”之分,前者是肯定(满足pattern则匹配成功),后者是否定(不满足pattern则匹配成功) 有一点需要注意:**逆向环视时,pattern长度必须是固定的**,也就是说pattern中不能出现诸如\*.等这类字符使得pattern匹配的长度不固定;但是前向环视没有这个要求(只是某些语言逆向环视只支持固定长度而已 推荐《精通正则表达式》) 断言的口诀: **前面有,正向后发(?<=exp),放前面;** **后面有(pattern),正向先行(?=exp),(pattern)放后面;** **前面无,反向后发(?<!exp),放前面;** **后面无,反向先行(?!exp),放后面。** 这个前面和后面是**针对目标字符串** python例子 ~~~ import re pattern = re.compile(r'(?<=<([a-zA-Z]+>)).*(?=</\1>)') s = '<html>hello world</html>' ret = re.search(pattern, s) print(ret.group()) #得到结果: #Traceback (most recent call last): # raise error("look-behind requires fixed-width pattern") #sre_constants.error: look-behind requires fixed-width pattern #python的re模块并不支持变长的后发断言,只支持定长的后发断言 #我们可以用分组来提取特定的字符串,上面代码给了.*增加了一个分组,按从左到右是第二个分组,这样我们可以在匹配结果中用.group(2)得到目标字符串 import re pattern = re.compile(r'<([a-zA-Z]+)>(.*)</\1>') s = '<html>hello world</html>' ret = re.search(pattern, s) print('re.group()→', ret.group()) print('re.group(2)→', ret.group(2)) #运行结果 #re.group()→ <html>hello world</html> #re.group(2)→ hello world ~~~ ``` #res=re.findall(r"(?<=define\([\'\"])\w+",fp.read()) #常量 #res=re.findall(r"\n[\s\;]*function[\s]+(([\w]+)[\s]*\(.*\))",fp.read())#函数 #res=re.findall(r"(?:public[\s]+function[\s]+|private[\s]+function[\s]+|protected[\s]+function[\s]+)(([\w]+)[\s]*\(.*\))",fp.read()) #类方法 ```