使用向前看和上后看你可以来完成各种各样的assert来满足你的需求。
在字符串处理时,你可以向前或者向后看,同时你可以让你所看的pattern是成功或者失败。
就像Java中的assert()
(?=pattern)
is a positive look-ahead assertion
assert(pattern match) –>
(?!pattern)
is a negative look-ahead assertion
assert(pattern not match) –>
(?<=pattern)
is a positive look-behind assertion
assert(pattern match) <--
(?<!pattern)
is a negative look-behind assertion
assert(pattern not match) <--
look-ahead –>向前看
look-behind <--向后看
1. 寻找最后出现的字符串
这里有很多不使用左右看的方法来完成寻找最后出现的字符串,
但是如果你需要的是:最后的字符串是foo并且foo之后再也没有foo字符串了(可以有其它的字符串),你可以使用:
/foo(?!.*foo)/
正则表达式引擎会在找到foo之后使用look-around中的pattern .*foo去匹配,如果匹配到,但是因为是getative的,
所有assert失败,然后正则表达式引擎会继续查找下一个foo。
例如:foobar会匹配到第一个foo
而foobarfooeee会匹配到第二个foo
以(?<=foo)作为分隔符
2. 替换之前,之后或者中间的字符
Many substitutions match a chunk of text and then replace part or all of it.
You can often avoid that by using look-arounds.
For example, if you want to put a comma after every foo:
很多替换方法匹配一串字符的全部或者一部分。你可以使用look-around来实现。
例如,你想要在每个foo之后都加一个逗号:
s/(?<=foo)/,/g; # 不使用向后看: s/foo/foo,/g or s/(foo)/$1,/g
或者给lookahead中间加一个-
s/(?<=look)(?=ahead)/-/g;
需要注意的是,左右看这种方法不能有变量长度,这意味着你不能在它之后或者之内使用像(?, *, +, or {1,5}) 这样的限定符。
将lookahead替换成look-ahead
3. 匹配一个pattern但是不包含其它的pattern
You might want to capture everything between foo and bar that doesn’t include
baz.
The technique is to have the regex engine look-ahead at every character to
ensure
that it isn’t the beginning of the undesired pattern:
你可能想匹配所有在foo和bar之间的字符串,但是foo之后不能是baz。
你可以使用下面的表达式:
/foo # Match starting at foo
( # Capture
(?: # Complex expression(you do not use the backreference):
(?!baz) # make sure we’re not at the beginning of baz
#or using (?!.baz) make sure the whole string between foo and bar not
contains baz
. # accept any character
) # any number of times
) # End capture
bar # and ending at bar
/x;
foo((?:(?!baz).*))bar
不会匹配foobazddbar
foo之后不知baz,所以匹配上了
如果想匹配foo和bar之间没有baz可以使用:
foo((?:(?!.baz).))bar
4. 嵌套
look-arounds是可以嵌套的。look-arounds的自表达式会冲它上层继承开始位置,然后在这个开始位置的基础上
左右看同时不影响上层的位置,子表达式和父亲是相互独立的,它们独自维护自己的位置信息。
这个概念很简单,但是表达式很快就会变的很难懂。所有推荐对正则表达式加注释。
下面让我们来看一个正则表达式的例子。我们希望在任何分隔符(为了简单我们只用,)之后加一个空格,
但是在两个数字之间的,不加空格。
s/(?<=, # after a comma,
(?! # but not matching
(?<=\d,) # digit-comma before, AND
(?=\d) # digit afterward
)
)/ /gx; # substitute a space
(?<=,(?!(?<=\d,)(?=\d)))
在,之后加空格,但是数字之间的不加
注意,我们可以使用多个lookaround来完成不同的条件。就像And或这Or。
事实上你可是使用布尔代数表达式 ( NOT (a AND b) === (NOT a OR NOT b) )
来转化上面的表达式
s/(?<=, # after a comma, but either
(?:
(?<!\d,) # not matching digit-comma before
| # OR
(?!\d) # not matching digit afterward
)
)/ /gx; # substitute a space
(?<=,(?:(?<!\d,)|(?!\d)))
4. 捕获
有时我们会在look-around中捕获大括号。你可能认为你不会用到。
但是还是请你记住: the capturing parentheses must be within the look-around expression。
从enclosing expression来看,lookaround其实没有进行匹配。
最有用的技巧是在全局匹配模式时寻找重叠的匹配。
你可以捕获子串但是不需要消耗它,然后它可以对后面的匹配有效。
或许最简单的例子就是获得所有的右边的子串
print “$1\n” while /(?=(.*))/g;
注意这个模式理论上不会消耗任何一个字符,但是Perl在空匹配时会自动追加一个字符来防止无限循环。
http://www.perlmonks.org/?node_id=518444