解决正则表达式中的“非”单词匹配问题
之前有过几次这样的面试经历。(都是网上面试,因为在这之前我一直还都是一个在校生,所以以上说的面试都是指面试那种可以SOHO兼职的)
考官给你一个地址让你打开那个网页,然后要求你用PHP抓取网页中某块他想要的内容。(题目就是这样)
当时我的第一反应就是“采集”,他要我做的就是采集。可是在这之前我都没有认真的系统的学过正则。这也就给我出了一个大难题。。。
虽然如此,我并没有直接叫他说我不会做,而是先做试试。这一做就是一个多小时。还没搞定,主要遇到的问题就是类似给定一个字符串”<div>aaa</div>bbb</div>”,然后要求你取得aaa这样的内容
其实在这之前我有一点点正则功底的,因为有用过一些简单的正则表达式,也看过正则的入门。。。所以抓取到源代码后解析出<div></div>这部分也不是难事,可难就难在,<div>(.*)</div>这中间的内容中有可能出现</div>而我要的是需要(.*)中的内容不能出现</div>,换句话说,我要的是一个完整的有开有闭的标签,但我的印象中,正则中的“非”操作好像只有[^asdfs]这类的,我也试过了这种方法,但发现根本不行,原因就是[^ab]这种“非”操作中的非指的是非a且非b而不是非ab,也就是说只要字符串中没有a且没有b则这个字符串就匹配,所以[^ab]*匹配ccccccadddb中的ccccc,这样就将单个的a和单个的b也排除在外了。而我要的功能则是排除一个”ab”这样的单词,我需要可以匹配单个a和单个b但不匹配单个”ab”的组合.最终以做不出来而告终~
第二次也还是遇到相同的题目,还是让我抓取他们想要的内容。。。
这次好一点了,上网搜索了很多正则的相关资料,后了解到了可以使用(?!单词|单词)这样的功能来取得,那时还不知道(?!)这样的操作叫什么名字,当时也就能实现后就这样做完了,没有去了解(?!)这个的工作原理
直到最近,自己写的一个模板引擎中又遇到了一个相同的问题。
终于在今天被解决啦。。哈哈
给定字符串”<div>aaa</div>bbb</div>”要取得<div>aaa</div>和aaa
可以使用/<div>(((?!).)*)<\/div>/i这样的模式来匹配他,而匹配得到的第一个值,就是<div>aaa</div>反向引用为\\0,而aaa则是\\2,
解释:
<div></div>这个限定了该模式是以<div>为头,以</div>为尾的字符串
(((?!<\/div>).)*)则是指字符串中不存在</div>这个组合的所有任意字符~
(?!单词|单词)这样的操作中反向预查,按官方的解释是
负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如’Windows (?!95|98|NT|2000)’ 能匹配 “Windows 3.1″ 中的 “Windows”,但不能匹配 “Windows 2000″ 中的 “Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
对于上面那段话我也是看了好多遍都无法理解。。。
以下是有大虾简单扼要的回答,也不知道正不正确,不过觉得还满像,满形象
?= 代表后面必须有
?! 代表后面不能有
?<=代表前面必须有
?<!代表前面不能有
目标串
windows 2000 is a os.
表达式 => 结果
/windows/ => window
/windows (?=98)/ => null
/windows (?=2000)/ => window
要求,我想匹配windows,但windows后面必需是2000才行,否则这个windows就不匹配.
windows 2000 符合
windows 98 不符合
注意:2000不在结果中,因为预查不消耗字符
的确是只有保存与否的差别。但我感觉你对保存与否还不太理解,不然应该不会这样问。保存与否并不只是一个结果的差别。
例如
$str=”winwinwin”;
如果你用win(win)的话,那找到第一个结果后,将从”win”开始查找下一个结果。结果只找到一个匹配。
如果你用win(?=win)的话,那找到第一个结果后,将从”winwin”开始查找。这样共找到2个匹配。
以上几段留着以后慢慢研究