用regexp的lookahead寻找符合pattern A但不符合pattern B的字串
偶而会需要找内含字串 A 但不含字串 B 的字串, 若刚好得用 regexp 表示的话, 会有点麻烦 (像是用 Android logcat filter 的时候)。查了一下, 发现 regular expression 有个强大的语法叫作 lookaround, 用它可相对容易地达到此需求, 还可以应付各种情况。详见 Regex Lookarounds: Lookahead and Lookbehind 的介绍。关键在于 lookaround 的语法只是从「目前的位置」往前或往后「看看是否符合目标 pattern」,不会实际占去符合的字串。看文件的例子会比较好理解。
以下是用 Python RE 比对「内含 abc 但 abc 之后不含 def 的字串」:
- In [70]: re.search('(?!.*def)abc', 'abcdef')
(?!.*def)abc 的意思是每一个位置都做以下的事:
先往后找看看有没有符合 .*def, 找不到才算成立。(?!...) 的意思是 negative lookahead
negative lookahead 成立后, 看看目前位置是否能找到 abc
反之, 下面这个写法是错的, 比对 abcdef 仍会有结果:
- In [69]: re.search('(?!def)abc', 'abcdef')
- Out[69]: <_sre.SRE_Match at 0x2f2fd98>
因为它的意思是在目前位置看看是否不符合 def, 于是一开始比对 abc 时就成立了, 然后再比对成功 abc, 于是回传比对成功的结果。
(?!.*def)abc 看起来很美好, 但它无法避开 defabc, 也就是 abc 之前有 def 的情况。虽然有 lookbehind 的语法, 但它只能比对固定长度的 pattern, 无法应付 xxxdefxxxabc, 所以得换个方式表示。
regex - Regular expression to match string not containing a word? 说明如何用 regexp 表示不含目标字串的字串, 并有图解说明运作的过程。了解之后, 可运用同样的技巧表示「内含 abc 但 abc 之前不含 def 的字串」:
- In [145]: re.search('^((?!def).)*abc', 'defabc')
(?!def) 检查目前位置是否不含 def
(?!def). 注意多加了一个 '.', 表示检查完后占去一个字元
((?!def).)* 比对 0 到多个符合 (?!def). 的字元
利用 regexp greedy 的特性, pattern 3 会尽可能占去符合这个的字元, 于是 ^((?!def.)*abc 就会比对出「内含 abc 但 abc 之前不含 def 的字串」。
再和一开始用的 lookahead 合在一起, 就能表示「内含 abc 但不含 def 的字串了」:
- In [147]: re.search('^((?!def).)*(?!.*def)abc', 'abcdef')
- In [148]: re.search('^((?!def).)*(?!.*def)abc', 'defabc')
- In [149]: re.search('^((?!def).)*(?!.*def)abc', 'abc')
- Out[149]: <_sre.SRE_Match at 0x2f2de40>
- In [150]: re.search('^((?!def).)*(?!.*def)abc', 'xxxabcxdefxx')
- In [151]: re.search('^((?!def).)*(?!.*def)abc', 'xdefxxabcxxx')
- In [152]:
就算一时之间无法消化也无所谓, 记得关键字 lookaround, lookahead, lookbehind, 之后比较方便找 regexp 进阶用法。每次找 regexp 的说明, 都会学到新东西, 真是博大精深的表示法。
2014-07-07日经网友 weiyu 提醒, 移动 abc 到中间效率会比较好, 以下是程式和测试结果:
- $ cat a.py
- import re
- import time
- begin = time.time()
- pattern = re.compile('^((?!def).)*(?!.*def)abc')
- for i in xrange(1000000):
- pattern.search('xxxxxxabcxxxxxx')
- pattern.search('xxxdefxxxabcxxxxxx')
- pattern.search('xxxxxxabcxxxdefxxx')
- print time.time() - begin
- begin = time.time()
- pattern = re.compile('^((?!def).)*abc(?!.*def)')
- for i in xrange(1000000):
- pattern.search('xxxxxxabcxxxxxx')
- pattern.search('xxxdefxxxabcxxxxxx')
- pattern.search('xxxxxxabcxxxdefxxx')
- print time.time() - begin
- $ python a.py
- 4.64261507988
- 3.08146381378
- 评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
-
