正则表达式负向前瞻

时间:2009-11-17 14:59:07

标签: regex lookahead negative-lookahead

在我的主目录中,我有一个文件夹drupal-6.14,其中包含Drupal平台。

从这个目录中我使用以下命令:

find drupal-6.14 -type f -iname '*' | grep -P 'drupal-6.14/(?!sites(?!/all|/default)).*' | xargs tar -czf drupal-6.14.tar.gz

此命令的作用是gzips文件夹 drupal-6.14 ,不包括 drupal-6.14 / sites / 的所有子文件夹,但sites / all和sites / default除外,包括它。

我的问题是关于正则表达式:

grep -P 'drupal-6.14/(?!sites(?!/all|/default)).*'

表达有效以排除我想要排除的所有文件夹,但我不太明白为什么。

使用正则表达式

是一项常见任务
  

匹配所有字符串,但包含子图案x的字符串除外。或者换句话说,否定子模式。

我(想)我明白解决这些问题的一般策略是使用负面的前瞻,但我从来没有理解到正面和负面的外观(前进/后退)是如何工作的。

多年来,我已经阅读了很多网站。 PHP和Python正则表达式手册,其他页面如http://www.regular-expressions.info/lookaround.html等等,但我从来没有真的对它们有充分的理解。

有人可以解释一下,这是如何工作的,也许可以提供一些类似的例子吗?

- 更新一次:

关于Andomar的回应:双重否定先行可以更简洁地表达为单一的积极前瞻性陈述:

即是:

'drupal-6.14/(?!sites(?!/all|/default)).*'

相当于:

'drupal-6.14/(?=sites(?:/all|/default)).*'

???

- 更新二:

根据@andomar和@alan moore - 你不能互换双阴性前瞻以获得积极的前瞻。

3 个答案:

答案 0 :(得分:101)

负向前瞻说,在这个位置,以下正则表达式无法匹配。

我们举一个简化的例子:

a(?!b(?!c))

a      Match: (?!b) succeeds
ac     Match: (?!b) succeeds
ab     No match: (?!b(?!c)) fails
abe    No match: (?!b(?!c)) fails
abc    Match: (?!b(?!c)) succeeds

最后一个例子是双重否定:它允许b后跟c。嵌套的否定前瞻变为积极的前瞻:c应该存在。

在每个示例中,只匹配a。前瞻只是一个条件,不会添加到匹配的文本中。

答案 1 :(得分:12)

Lookarounds可以嵌套。

因此,此正则表达式匹配“drupal-6.14 /”,即,后跟后跟“/ all”或“/ default”的“网站”。

混淆?使用不同的单词,我们可以说它匹配“drupal-6.14 /”,后跟“sites”,除非后面跟着“/ all”或“/默认的“

答案 2 :(得分:2)

如果你像这样修改正则表达式:

drupal-6.14/(?=sites(?!/all|/default)).*
             ^^

...然后它会匹配包含drupal-6.14/后跟sites后跟 /all/default以外的所有输入。例如:

drupal-6.14/sites/foo
drupal-6.14/sites/bar
drupal-6.14/sitesfoo42
drupal-6.14/sitesall

?=更改为?!以匹配原始正则表达式,只会取消这些匹配:

drupal-6.14/(?!sites(?!/all|/default)).*
             ^^

因此,这只是意味着drupal-6.14/现在不能后跟sites后跟 /all或{之外的任何内容{1}}。所以现在,这些输入将满足正则表达式:

/default

但是,从其他一些答案(可能是您的问题)中可能不明显的是,您的正则表达式还将允许其他输入,其中drupal-6.14/sites/all drupal-6.14/sites/default drupal-6.14/sites/all42 后跟除了drupal-6.14/也是如此。例如:

sites

结论:因此,您的正则表达式基本上是要包含drupal-6.14/foo drupal-6.14/xsites 所有子目录,但 {{1}的子目录除外其名称以drupal-6.14sites以外的任何内容开头。