如何在 Perl 6 中 negate/subtract 正则表达式(不仅仅是字符 类)?
How to negate/subtract regexes (not only character classes) in Perl 6?
可以创建 conjunction,使字符串匹配 2 个或更多 regex
模式。
> "banana" ~~ m:g/ . a && b . /
(「ba」)
此外,可以negate a character class:如果我只想匹配辅音,我可以取所有字母并减去元音字符class:
> "camelia" ~~ m:g/ <.alpha> && <-[aeiou]> /
(「c」 「m」 「l」)
但是如果我需要 negate/subtract 不是字符 class,而是任意长度的 regex
怎么办?像这样:
> "banana" ~~ m:g/ . **3 && NOT ban / # doesn't work
(「ana」)
TL;DR Moritz 的回答涵盖了一些重要问题。该答案侧重于根据 Eugene 的评论匹配子字符串 ("I want to find substring(s) that match regex R
, but don't match regex A
.")。
在 您 不想 想要匹配的正则表达式之前写一个断言,表明您不会立即坐下,然后跟随着您 想要匹配的正则表达式:
say "banana" ~~ m:g/ <!before ban> . ** 3 / # (「ana」)
before
断言称为 "zero width" 断言。这意味着如果它成功了(在这种情况下意味着它 而不是 "match" 因为我们写的是 !before
而不仅仅是 before
),匹配的位置没有移动
(当然,如果这样的断言失败并且在当前匹配位置没有匹配的替代模式,则匹配引擎然后前进一个字符位置。)
您可能需要相反顺序的模式,首先是正匹配,然后是负匹配,如您在问题中所示。 (也许正匹配比负匹配快,所以颠倒他们的顺序会加快匹配。)
一种适用于相当简单模式的方法是使用否定 after 断言:
say "banana" ~~ m:g/ . ** 3 <!after ban> / # (「ana」)
但是,如果否定模式足够复杂,您可能需要使用此公式:
say "banana" ~~ m:g/ . ** 3 && <!before ban> .*? / # (「ana」)
这会插入一个 &&
regex conjunction operator,假定 LHS 模式成功,也会尝试 RHS 在重置匹配位置后 (这就是为什么 RHS 现在以<!before ban>
而不是 <!after ban>
),并且要求 RHS 匹配相同长度的输入(这就是为什么 <!before ban>
后面跟着 .*?
"padding")。
它对 "negate" 正则表达式意味着什么?
当您谈论正则表达式的计算机科学定义时,它总是需要匹配整个字符串。在这种情况下,否定很容易定义。但默认情况下,Perl 6 中的正则表达式 search,因此它们不必匹配整个字符串。这意味着您必须小心定义 "negate".
的含义
如果正则表达式 A
的否定意味着当 A
不匹配整个字符串时匹配的正则表达式,反之亦然,您确实可以使用 <!before ...>
,但你需要小心锚定:/ ^ <!before A $ > .* /
是这个确切的否定。
如果通过否定正则表达式 A
你的意思是 "only match if A
matches nowhere in the string",你必须使用像 / ^ [<!before A> .]* $ /
.
这样的东西
如果您对否定有其他定义,请分享。
可以创建 conjunction,使字符串匹配 2 个或更多 regex
模式。
> "banana" ~~ m:g/ . a && b . /
(「ba」)
此外,可以negate a character class:如果我只想匹配辅音,我可以取所有字母并减去元音字符class:
> "camelia" ~~ m:g/ <.alpha> && <-[aeiou]> /
(「c」 「m」 「l」)
但是如果我需要 negate/subtract 不是字符 class,而是任意长度的 regex
怎么办?像这样:
> "banana" ~~ m:g/ . **3 && NOT ban / # doesn't work
(「ana」)
TL;DR Moritz 的回答涵盖了一些重要问题。该答案侧重于根据 Eugene 的评论匹配子字符串 ("I want to find substring(s) that match regex R
, but don't match regex A
.")。
在 您 不想 想要匹配的正则表达式之前写一个断言,表明您不会立即坐下,然后跟随着您 想要匹配的正则表达式:
say "banana" ~~ m:g/ <!before ban> . ** 3 / # (「ana」)
before
断言称为 "zero width" 断言。这意味着如果它成功了(在这种情况下意味着它 而不是 "match" 因为我们写的是 !before
而不仅仅是 before
),匹配的位置没有移动
(当然,如果这样的断言失败并且在当前匹配位置没有匹配的替代模式,则匹配引擎然后前进一个字符位置。)
您可能需要相反顺序的模式,首先是正匹配,然后是负匹配,如您在问题中所示。 (也许正匹配比负匹配快,所以颠倒他们的顺序会加快匹配。)
一种适用于相当简单模式的方法是使用否定 after 断言:
say "banana" ~~ m:g/ . ** 3 <!after ban> / # (「ana」)
但是,如果否定模式足够复杂,您可能需要使用此公式:
say "banana" ~~ m:g/ . ** 3 && <!before ban> .*? / # (「ana」)
这会插入一个 &&
regex conjunction operator,假定 LHS 模式成功,也会尝试 RHS 在重置匹配位置后 (这就是为什么 RHS 现在以<!before ban>
而不是 <!after ban>
),并且要求 RHS 匹配相同长度的输入(这就是为什么 <!before ban>
后面跟着 .*?
"padding")。
它对 "negate" 正则表达式意味着什么?
当您谈论正则表达式的计算机科学定义时,它总是需要匹配整个字符串。在这种情况下,否定很容易定义。但默认情况下,Perl 6 中的正则表达式 search,因此它们不必匹配整个字符串。这意味着您必须小心定义 "negate".
的含义如果正则表达式 A
的否定意味着当 A
不匹配整个字符串时匹配的正则表达式,反之亦然,您确实可以使用 <!before ...>
,但你需要小心锚定:/ ^ <!before A $ > .* /
是这个确切的否定。
如果通过否定正则表达式 A
你的意思是 "only match if A
matches nowhere in the string",你必须使用像 / ^ [<!before A> .]* $ /
.
如果您对否定有其他定义,请分享。