(* SKIP)或(* F)如何在正则expression式上工作?

我正在学习正则expression式的高级用法,并注意到许多post都使用(*SKIP)(*F)

我发布了一个问题,其中的想法是匹配没有yellow但只有在蓝色之后存在brown蓝色的行。 正确的答案是:

 .*yellow.*(*SKIP)(*F)|^.*\bblue\b(?=.*brown).*$ 

我也试过像下面这样的查找expression式,但并没有为所有的情况工作:

 ^((?!yellow).)*blue(?=.*brown).*$ 

我不知道这些(*SKIP)(*F)标志,所以问题是,这些标志是如何工作的? 他们在做什么? 还有其他像这样的标志吗?

谢谢。

这两个回溯控制动词只在Perl,PCRE和pypi regex模块中实现 。

(*SKIP)(*FAIL)技巧的想法是消耗你想要避免的字符,而且不能是匹配结果的一部分。

使用这种技巧的古典模式如下所示:

 What_I_want_to_avoid(*SKIP)(*FAIL)|What_I_want_to_match 

正则expression式引擎处理这样一个string:

  • 模式的第一个标记从左到右testing每个字符(默认情况下,大部分时间,但一些正则expression式引擎可以设置为从右到左工作,.net可以做到这一点,如果我没记错的话)

  • 如果第一个标记匹配,那么正则expression式引擎会testing下一个字符(在第一个标记匹配之后)的下一个标记

  • 当一个令牌失败时,正则expression式引擎获取最后一个令牌匹配的字符,并尝试另一种方式使得该模式成功(如果它不工作,则正则expression式引擎也会和前一个令牌一样)

当正则expression式引擎遇到(*SKIP)动词时(在这种情况下,所有以前的令牌显然成功了) ,它没有权利回到所有先前的令牌,并且没有权利重试所有的如果模式在(*SKIP)动词右侧后面失败,则直到最后一个匹配的字符(包含)

(*FAIL)的作用是强制模式失败。 因此,在(*SKIP)左边匹配的所有字符都被跳过,正则expression式引擎在这些字符之后继续工作。

在示例模式中,模式成功的唯一可能性是第一个分支在(*SKIP)之前失败,以允许testing第二个分支。

你可以在这里find另一种解释。

关于没有这两个function的 Java 和其他正则expression式引擎

其他正则expression式引擎中没有实现回溯控制动词,并且没有等价物。

然而,你可以用几种方法来做同样的事情(要更清楚一些,以避免可能被模式的其他部分匹配的东西)

捕获组的使用:

方式1:

 What_I_want_to_avoid|(What_I_want_to_match) 

您只需要提取捕获组1 (或者testing它是否存在) ,因为这是您正在查找的内容。 如果使用该模式执行replace,则可以使用匹配结果(偏移量,长度,捕获组)的属性使用经典string函数进行replace。 其他语言如JavaScript,ruby…允许使用callback函数作为替代。

方式2:

 ((?>To_avoid|Other_things_that_can_be_before_what_i_want)*)(What_I_want) 

这是更换简单的方法,不需要callback函数,replacestring只需要以\1 (或$1开始,

lookarounds的使用:

例如,你想find一个没有embedded其他两个单词之间的单词(可以说S_wordE_word是不同的(参见Qtax评论) ):

(在这个例子中边缘情况S_word E_word word E_wordS_word word S_word E_word是允许的。)

回溯控制动词的方式是:

 S_word not_S_word_or_E_word E_word(*SKIP)(*F)|word 

为了使用这种方式,正则expression式引擎需要在一定程度上允许可变长度的lookbehinds。 使用.net或新的正则expression式模块,没有问题,lookbehinds可以有一个完全可变的长度。 Java也可以,但是大小必须是有限的(例如: (?<=.{1,1000})

Java的等价物将是:

 word(?:(?!not_S_word_or_E_word E_word)|(?<!S_word not_E_word{0,1000} word)) 

请注意,在某些情况下,只有前瞻是必要的。 还要注意,用字面字符开始一个模式比从一个逆序开始更有效率,这就是为什么我把它放在单词之后(即使我需要在断言中再次重写单词)。

(*SKIP)(*F) (aka *FAIL )模式logging在Perl手册中: http : //perldoc.perl.org/perlre.html

但是,它们只能用于Perl以及模仿Perl的正则expression式(例如PHP使用的PCRE库)。

Java的内置正则expression式引擎不支持这些扩展,我不知道这样做。

我在Java中的一般build议是保持你的正则expression式简单,并使用其他的string操作方法来实现一个简短的正则expression式无法完成的事情。