【问题标题】:How do (*SKIP) or (*F) work on regex?(*SKIP) 或 (*F) 如何处理正则表达式?
【发布时间】:2026-01-16 23:20:06
【问题描述】:

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

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

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

我也尝试过像下面这样的环视表达式,但并非适用于所有情况:

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

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

谢谢。

【问题讨论】:

  • @SotiriosDelimanolis 实际上我没有在 java 模式上使用它我正在 regex101 上测试它
  • @SotiriosDelimanolis 是的,你是对的,但想法是稍后在 java 上实现这个正则表达式。首先我想学习正则表达式的方式。顺便说一句,你推荐我用什么标签而不是 java,因为我在 regex101 上做它?
  • @TheLostMind 无需道歉,我不想让任何人感到困惑。感谢您的修复。
  • 这些标志是 Perl 兼容正则表达式 (PCRE) 的一个特性,所以我建议阅读它的 documentation(在文档中搜索您想了解的标签)。为了使用它们,您需要找到一个支持它们以用于您选择的语言的正则表达式库。我不知道有任何这样的 Java 库。
  • @Fede:我认为(*SKIP)(*F) 不会在 Java 上工作。不过,Java 中还有其他 hack 方法可以绕过 Java 中的可变长度回溯。

标签: regex


【解决方案1】:

这两个回溯控制动词仅在 Perl、PCRE 和pypi regex module 中实现。

(*SKIP)(*FAIL) 技巧的想法是使用您想要避免的字符,并且这些字符不能成为匹配结果的一部分。

使用此技巧的经典模式如下所示:

What_I_want_to_avoid(*SKIP)(*FAIL)|What_I_want_to_match

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

  • 模式的第一个标记在每个字符上从左到右进行测试(默认情况下大部分时间,但一些正则表达式引擎可以设置为从右到左工作,.net 可以如果我没记错的话)

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

  • 1234563与之前的令牌等)

当正则表达式引擎遇到(*SKIP)动词(在这种情况下所有先前的标记显然都成功了),它没有权利再回到左边的所有先前的标记并且已经如果模式稍后在 @987654326 右侧失败,则不再有权使用模式的另一个分支或字符串中的下一个位置重试所有匹配的字符,直到最后一个匹配的字符 (included) @动词。

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

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

你可以找到另一种解释here

关于 Java 和其他不具备这两个功能的正则表达式引擎

回溯控制动词未在其他正则表达式引擎中实现,也没有等效项。

但是,您可以使用几种方法来做同样的事情(更清楚地说,要避免可能被模式的其他部分匹配的东西)

捕获组的使用:

方式一:

What_I_want_to_avoid|(What_I_want_to_match)

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

方式2:

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

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

环视的使用:

例如,您想查找未嵌入在其他两个单词之间的单词(假设 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

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

Java 等价物是:

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

请注意,在某些情况下,只需要先行。还要注意,以文字字符开头的模式比从后向开头更有效,这就是为什么我把它放在单词 之后(即使我需要在断言中再次重写这个单词。)

【讨论】:

  • 很好的解释。这就是我正在寻找的解释。我正在java中寻找这些行为,所以我想我会发布另一个问题。
  • @Fede:我将添加一个关于没有这些功能的 Java 的补充。
  • 谢谢。这个很有用,可惜java的引擎不支持这个,很好用
  • @CasimiretHippolyte 如何在 What_I_want_to_avoid 左侧指定要匹配的内容?例如,我想匹配“blah”这个词,而不是“blaah”。如何将bl 合并到aa(*SKIP)(*F)|ah 中?
  • "The use of lookarounds" 示例并不完全正确,lookahead 表达式与 (*SKIP)(*F) 表达式匹配的字符串不匹配,它也没有做什么示例文本状态。例如字符串word E_word 中的word 应该匹配,但前瞻表达式不匹配。
【解决方案2】:

(*SKIP)(*F)(又名*FAIL)模式记录在 Perl 手册中:http://perldoc.perl.org/perlre.html

但是,它们仅在 Perl 和模仿 Perl 的正则表达式风格中可用(例如 PHP 使用的 PCRE 库)。

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

我在 Java 中的一般建议是让您的正则表达式保持简单,并使用其他字符串操作方法来实现短正则表达式无法完成的任务。

【讨论】:

  • 在 php 中确认可用性。