"可变长度lookbehind未实现",但它不是可变长度

Ste*_*hen 56 regex perl

我有一个非常疯狂的正则表达式,我正在尝试诊断.它也很长,但我把它简化为以下脚本.使用Strawberry Perl v5.26.2运行.

use strict;
use warnings;

my $text = "M Y H A P P Y T E X T";
my $regex = '(?i)(?<!(Mon|Fri|Sun)day |August )abcd(?-i)';

if ($text =~ m/$regex/){
    print "true\n";
}
else {
    print "false\n";
}
Run Code Online (Sandbox Code Playgroud)

这给出了错误"正则表达式中没有实现可变长度的后备".

我希望你能帮助解决几个问题:

  1. 我不明白为什么会出现这种错误,因为所有可能的lookbehind值都是7个字符:"Monday","Friday","Sunday","August".
  2. 我自己没有写这个正则表达式,我不知道如何解释语法(?i)(?-i).当我摆脱(?i)错误实际上消失了.perl将如何解释这部分正则表达式?我认为前两个字符被评估为"可选的文字括号",除了圆括号没有被转义,在这种情况下我会得到不同的语法错误,因为不会匹配右括号.
  3. 此行为从Perl 5.16.3_64和5.26.1_64之间开始,至少在Strawberry Perl中.前一个版本的代码很好,后者则没有.它为什么开始?

anu*_*ava 76

我把你的问题减少到了这个:

my $text = 'M Y H A P P Y T E X T';
my $regex = '(?<!st)A';
print ($text =~ m/$regex/i ? "true\n" : "false\n");
Run Code Online (Sandbox Code Playgroud)

由于/i(不区分大小写)修饰符的存在以及某些字符组合的存在,例如"ss"或者"st"可以由Typographic_ligature替换,从而使其成为可变长度(/August/i例如匹配两个AUGUST(6个字符)和augu?(5个字符,最后一个)是U + FB06)).

但是,如果我们删除/i(不区分大小写)修饰符,那么它的工作原理是因为印刷连字不匹配.

解决方案:使用aa修饰符,即:

/(?<!st)A/iaa
Run Code Online (Sandbox Code Playgroud)

或者在你的正则表达式中:

my $text = 'M Y H A P P Y T E X T';
my $regex = '(?<!(Mon|Fri|Sun)day |August )abcd';
print ($text =~ m/$regex/iaa ? "true\n" : "false\n");
Run Code Online (Sandbox Code Playgroud)

来自perlre:

要禁止ASCII /非ASCII匹配(例如"k"和"\ N {KELVIN SIGN}"),请指定"a"两次,例如/aai/aia.(第一次出现"a"限制\d等等,第二次出现会增加"/ i"限制.)但是,请注意,ASCII范围之外的代码点将使用Unicode规则进行/i匹配,因此修饰符不会真的把事情限制在ASCII; 它只是禁止ASCII和非ASCII的混合.

请参阅此处密切相关的讨论

  • 为此做出贡献:[perldiag`]中也提到了它(https://perldoc.perl.org/perldiag.html#Variable-length-lookbehind-not-implemented-in-regex-m/%s/): "在`/ i`下有一些非显而易见的Unicode规则可以不断匹配,但你可能认为不可能." 除了`/ aa`开关之外,另一个解决方案可能是反转字符串并使用前瞻,如果您需要Unicode支持和可变长度外观. (10认同)
  • 用`[s]`或`[t]`替换`s`或`t`似乎也解决了这个问题. (3认同)

cho*_*oba 21

那是因为st可以是一个结扎线.同样的情况,以fiff:

#!/usr/bin/perl
use warnings;
use strict;

use utf8;

my $fi = '?';
print $fi =~ /fi/i;
Run Code Online (Sandbox Code Playgroud)

因此,想象一下?|fi,事实上,替代品的长度并不相同.