我有一个非常疯狂的正则表达式,我正在尝试诊断。它也很长,但我把它简化为以下脚本。使用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";
}
这给出了错误“正则表达式中没有实现可变长度的后备。”
我希望你能解决几个问题:
(?i)
和(?-i)
。当我摆脱(?i)
时,错误实际上消失了。 perl将如何解释这部分正则表达式?我认为前两个字符被评估为“可选的字面括号”,但括号不会被转义,在这种情况下我会得到不同的语法错误,因为不会匹配右括号。答案 0 :(得分:75)
我已将问题减少到:
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");
由于存在/i
(不区分大小写)修饰符并且存在某些字符组合,例如"ss"
或"st"
,可以由Typographic_ligature替换,导致它是一个可变长度(/August/i
匹配,例如AUGUST
(6个字符)和august
(5个字符,最后一个是U + FB06)。
但是,如果我们删除/i
(不区分大小写)修饰符,那么它的工作原理是因为印刷连字不匹配。
解决方案:使用aa
修饰符,即:
/(?<!st)A/iaa
或者在你的正则表达式中:
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");
来自perlre:
要禁止ASCII /非ASCII匹配(例如“k”与“\ N {KELVIN SIGN}”),请指定“a”两次,例如
/aai
或/aia
。 (第一次出现“a”限制\d
等,第二次出现会增加“/ i”限制。)但是,请注意,ASCII范围之外的代码点将使用{{1的Unicode规则匹配,所以修饰符并不真正将事物限制为ASCII; 它只是禁止ASCII和非ASCII的混合。
答案 1 :(得分:21)
那是因为st
可以是一个结扎线。 fi
和ff
:
#!/usr/bin/perl
use warnings;
use strict;
use utf8;
my $fi = 'fi';
print $fi =~ /fi/i;
想象一下像fi|fi
之类的东西,确实,替代品的长度并不相同。
答案 2 :(得分:2)
st
可以用st
或ſt
表示为1个字符stylistic ligature,因此其长度可以是2或1。
使用bash命令快速查找perl的2→1字符连字的完整列表:
$ perl -e 'print $^V'
v5.26.2
$ for lig in {a..z}{a..z}; do \
perl -e 'print if /(?<!'$lig')x/i' 2>/dev/null || echo $lig; done
ff fi fl ss st
这些分别代表ff
,fi
,fl
,ß
和st
/ ſt
连字。
(ſt
代表ſt
,使用过时的long s character;它与st
匹配,而不匹配{{ 1}}。)
Perl还支持ft
和ffi
的剩余文体连字,ffl
和ffi
,尽管在这种情况下这并不值得注意,因为看起来已经存在问题<{1}}和ffl
/ ff
分开。
perl的未来版本可能包含更多样式的连字,但所有剩余的都是特定于字体的(例如Linux Libertine具有fi
和ch
的风格连字)或具有风格的格式(例如fl
的荷兰语ij
或ct
的过时西班牙语ꝇ
。对于不完全可互换的连线进行此处理似乎不合适(没有人会接受ij
ll
),尽管还有其他情况,例如包括dœs
感谢它的uppercase form being SS
。
Perl 5.16.3(以及类似的旧版本)仅在does
(对于ß
)偶然发现并且无法扩展外观中的其他连字(它们具有固定宽度且不匹配)。我没有找出错误修正来详细列出哪些版本受到影响。
Perl 5.14引入了连字支持,因此早期版本没有这个问题。
ss
的解决方法(只有第一个会正确避免ß
):
/(?<!August)x/i
(绝对全面)August
(只是后视中的/(?<!Augus[t])(?<!Augu(?=st).)x/i
是“ASCII-safe”²)/(?<!Augu(?aa:st))x/i
(整个后方是“ASCII-safe”²)st
(整个正则表达式是“ASCII-safe”²)/(?<!(?aa)August)x/i
(打破结扎寻求¹)/(?<!August)x/iaa
(略有不同,匹配更多)/(?<!Augus[t])x/i
(区分大小写的/(?<!Augus.)x/i
,与/(?<!Augu(?-i:st))x/i
不匹配)这些玩具删除不区分大小写的修饰符¹或在各个地方添加 ASCII安全修饰符²,通常要求正则表达式编写者专门知道变量-width ligature。
第一个变体(它是唯一的综合变体)将变量宽度与两个lookbehinds匹配:首先是六个字符版本(没有连字,如下面的第一个引用中所述),第二个是任何连字,使用 st
的前向预测(宽度为零!)(包括连字),然后用AugusTx
perlre
man page的两个部分:
st
&amp;连字有许多Unicode字符与序列匹配
.
下的多个字符。例如,“LATIN SMALL LIGATURE FI“应该与序列/i
匹配.Perl目前无法使用 当多个字符在模式中时,执行此操作 分组之间,或者当一个或多个被量化时。因此/i
fi
(perl 5.14 +)禁止ASCII /非ASCII匹配(例如
"\N{LATIN SMALL LIGATURE FI}" =~ /fi/i; # Matches [in perl 5.14+] "\N{LATIN SMALL LIGATURE FI}" =~ /[fi][fi]/i; # Doesn't match! "\N{LATIN SMALL LIGATURE FI}" =~ /fi*/i; # Doesn't match! "\N{LATIN SMALL LIGATURE FI}" =~ /(f)(i)/i; # Doesn't match!
与/aa
), 指定k
两次,例如\N{KELVIN SIGN}
或a
。 (首先/aai
的出现限制了/aia
等,第二次出现 添加a
限制。)但是,请注意代码指向外部 ASCII范围将使用Unicode规则进行\d
匹配,因此修饰符 并没有真正将事情限制在ASCII;它只是禁止了 ASCII和非ASCII的混合。总而言之,此修饰符为应用程序提供保护 不希望暴露于所有Unicode。指定它两次给出 增加了保护。
答案 3 :(得分:0)
在lookbehind之后放置(?i)
:
(?<!(Mon|Fri|Sun)day |August )(?i)abcd(?-i)
或
(?<!(Mon|Fri|Sun)day |August )(?i:abcd)
对我而言,这似乎是一个错误。