(*SKIP)或(*F)如何处理正则表达式?

Fed*_*zza 26 regex

我正在学习正则表达式的高级用法,并注意到许多帖子使用(*SKIP)(*F)在其中.

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

.*yellow.*(*SKIP)(*F)|^.*\bblue\b(?=.*brown).*$
Run Code Online (Sandbox Code Playgroud)

我也尝试了下面的外观表达式但是并没有适用于所有情况:

^((?!yellow).)*blue(?=.*brown).*$
Run Code Online (Sandbox Code Playgroud)

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

谢谢.

Cas*_*yte 47

这两个回溯控制动词仅在Perl,PCRE和pypi正则表达式模块中实现.

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

使用这个技巧的经典模式看起来像这样:

What_I_want_to_avoid(*SKIP)(*FAIL)|What_I_want_to_match
Run Code Online (Sandbox Code Playgroud)

正则表达式引擎处理这样的字符串:

  • 从左到右对每个字符测试模式的第一个标记(默认情况下大部分时间,但是一些正则表达式引擎可以设置为从右到左工作,.net可以这样做,如果我记得很清楚)

  • 如果第一个标记匹配,则正则表达式引擎使用下一个字符(在第一个标记匹配之后)测试模式的下一个标记.

  • 当令牌失败时,正则表达式引擎获取与最后一个令牌匹配的字符并尝试另一种方式使该模式成功(如果它也不起作用,则正则表达式引擎对前一个令牌执行相同操作等)

当正则表达式引擎遇到(*SKIP)动词时(在这种情况下,所有以前的令牌显然都已成功),它没有权利返回到左侧所有以前的令牌,并且没有权利重新使用所有匹配的字符模式的其他分支或字符串中的下一个位置,直到最后一个匹配的字符(包括),如果该模式稍后在(*SKIP)动词右侧失败.

其作用(*FAIL)是强制模式失败.因此,(*SKIP)跳过左侧匹配的所有字符,并且正则表达式引擎在这些字符之后继续其作业.

模式在示例模式中成功的唯一可能性是第一个分支在(*SKIP)允许第二个分支被测试之前失败.

你可以在这里找到其他类型的解释.

关于Java 和其他没有这两个功能的正则表达式引擎

回溯控制动词没有在其他正则表达式引擎中实现,也没有相应的.

但是,您可以使用多种方法来执行相同操作(更清楚,以避免可能与模式的其他部分匹配的内容).

捕获组的使用:

方式1:

What_I_want_to_avoid|(What_I_want_to_match)
Run Code Online (Sandbox Code Playgroud)

您只需要提取捕获组1 (或测试它是否存在),因为它正是您要查找的.如果使用模式执行替换,则可以使用匹配结果的属性(偏移量,长度,捕获组)来替换经典字符串函数.其他语言如javascript,ruby ...允许使用回调函数作为替换.

方式2:

((?>To_avoid|Other_things_that_can_be_before_what_i_want)*)(What_I_want)
Run Code Online (Sandbox Code Playgroud)

这是替换更简单的方法,不需要回调函数,替换字符串只需要以(或)开头\1 $1

使用lookarounds:

例如,你想找到一个没有嵌入两个其他单词之间的单词(比方说S_word,E_word那就是不同的(参见Qtax评论)):

(边缘情况S_word E_word word E_word,S_word word S_word E_word在此示例中是允许的.)

回溯控制动词的方式是:

S_word not_S_word_or_E_word E_word(*SKIP)(*F)|word
Run Code Online (Sandbox Code Playgroud)

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

Java等价物将是:

word(?:(?!not_S_word_or_E_word E_word)|(?<!S_word not_E_word{0,1000} word))
Run Code Online (Sandbox Code Playgroud)

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

  • 优秀的解释.这就是我要找的解释.我在java中寻找这些行为,所以我想我会发布另一个问题. (2认同)

sli*_*lim 5

(*SKIP)(*F)(又名*FAIL)图案在Perl的手册记载:http://perldoc.perl.org/perlre.html

但是,它们只能在Perl中使用,并且可以模仿Perl的正则表达式(例如PHP使用的PCRE库).

Java的内置正则表达式引擎不支持这些扩展,我不知道有哪些.

我在Java中的一般建议是保持你的正则表达式简单,并使用其他字符串操作方法来实现短正则表达式无法做到的事情.