Regex Perl正则表达式多重匹配
我正在寻找一个正则表达式,其行为如下: 输入:“你好,世界。” 输出:he、el、ll、lo、wo或rl、ld 我的想法大致是Regex Perl正则表达式多重匹配,regex,perl,backreference,Regex,Perl,Backreference,我正在寻找一个正则表达式,其行为如下: 输入:“你好,世界。” 输出:he、el、ll、lo、wo或rl、ld 我的想法大致是 while($string =~ m/(([a-zA-Z])([a-zA-Z]))/g) { print "$1-$2 "; } 但这有点不同。这很棘手。你必须捕获它,保存它,然后强制回溯 您可以这样做: use v5.10; # first release with backtracking control verbs my $
while($string =~ m/(([a-zA-Z])([a-zA-Z]))/g) {
print "$1-$2 ";
}
但这有点不同。这很棘手。你必须捕获它,保存它,然后强制回溯 您可以这样做:
use v5.10; # first release with backtracking control verbs
my $string = "hello, world!";
my @saved;
my $pat = qr{
( \pL {2} )
(?{ push @saved, $^N })
(*FAIL)
}x;
@saved = ();
$string =~ $pat;
my $count = @saved;
printf "Found %d matches: %s.\n", $count, join(", " => @saved);
产生以下结果:
找到8个匹配项:he、el、ll、lo、wo、or、rl、ld。
如果您没有v5.10或头痛,可以使用以下方法:
my $string = "hello, world!";
my @pairs = $string =~ m{
# we can only match at positions where the
# following sneak-ahead assertion is true:
(?= # zero-width look ahead
( # begin stealth capture
\pL {2} # save off two letters
) # end stealth capture
)
# succeed after matching nothing, force reset
}xg;
my $count = @pairs;
printf "Found %d matches: %s.\n", $count, join(", " => @pairs);
这将产生与以前相同的输出
但是你可能还是会头疼。我会在“前瞻”中使用捕获的
组
(?=([a-zA-Z]{2}))
------------
|->group 1 captures two English letters
试试看无需“强制回溯”
尽管您可能希望匹配任何字母,而不是指定的有限集合
push @pairs, "$1$2" while /(\pL)(?=(\pL))/g;
您可以通过查找字母并使用pos
函数利用捕获的位置,\G
在另一个正则表达式中引用它,以及substr
从字符串中读取几个字符来完成此操作
use v5.10;
use strict;
use warnings;
my $letter_re = qr/[a-zA-Z]/;
my $string = "hello world.";
while( $string =~ m{ ($letter_re) }gx ) {
# Skip it if the next character isn't a letter
# \G will match where the last m//g left off.
# It's pos() in a regex.
next unless $string =~ /\G $letter_re /x;
# pos() is still where the last m//g left off.
# Use substr to print the character before it (the one we matched)
# and the next one, which we know to be a letter.
say substr $string, pos($string)-1, 2;
}
您可以将“检查下一个字母”逻辑放入带有零宽度肯定断言的原始正则表达式中,(?=pattern)
。零宽度意味着它不会被捕获,也不会提升m//g
regex的位置。这有点紧凑,但零宽度断言可能会变得棘手
while( $string =~ m{ ($letter_re) (?=$letter_re) }gx ) {
# pos() is still where the last m//g left off.
# Use substr to print the character before it (the one we matched)
# and the next one, which we know to be a letter.
say substr $string, pos($string)-1, 2;
}
更新:我最初试图将匹配和前瞻捕获为m{($letter_re(?=$letter_re))}gx
,但没有成功。向前看是零宽度并且滑出比赛。其他人的回答表明,如果你在“展望”中加入第二个捕获,那么它可能会崩溃到
say "$1$2" while $string =~ m{ ($letter_re) (?=($letter_re)) }gx;
我把所有答案留给TMTOWTDI,特别是如果你不是正则表达式大师的话。还有另一种方法。不使用任何regexp魔术,它确实使用嵌套的map
s,但如果需要,可以很容易地将其转换为for
循环
#!/usr/bin/env perl
use strict;
use warnings;
my $in = "hello world.";
my @words = $in =~ /(\b\pL+\b)/g;
my @out = map {
my @chars = split '';
map { $chars[$_] . $chars[$_+1] } ( 0 .. $#chars - 1 );
} @words;
print join ',', @out;
print "\n";
同样,对我来说,这比一个奇怪的正则表达式YMMV更易读。好问题。我想我以前可能已经回答过了。对于(*失败)
。回答得不错。你能简单地解释一下答案吗?@KrishnachandraSharma,嗯,我想我有。捕获它,将其保存在外部变量中,然后强制失败。故障回溯,引擎向前一步,因为它没有进展,我们再试一次。起泡、冲洗、重复。很好的方法和答案,但这在旧版本的Perl 5.10中会出现问题。谢谢,我认为会有一种更直接的方法,但它完成了工作。(?{})
在编译regex时捕获,这是在编译时。因此,这使它像一个命名的sub。在将其移动到sub之前,实际上没有问题,但当您移动到sub时,您将得到变量“@saved”不可用
效果很好。回答得好,“字母表”是什么?希腊语是字母表。拉丁语是字母表。西里尔语是一种字母表。不捕捉“字母表”;捕获A和Z(包括A和Z)之间的两个大写或小写字母。它在立面之类的东西上失败了。@tchrist op从来没有提到过……a-zA-Z仍然是alphabets@Some1.Kill.The.DJ嗯,这不是英文字母表的意思。我建议你查一本字典。@tchrist hope英文字母表让你更清楚:首先,PNot效率不高。你知道,那有可怕的“[a-Z]
代码气味”。有利于避免墨西哥辣椒,不利于正常文本。@tchrist,op从未提及任何关于字母的内容,但增加了\pL
。我总是使用\pL
,除非我在专门规定a-Z的RFC工作。我希望我记得(*FAIL)
在前瞻技术之外还有什么好处。@tchrist,也用于条件句(?({condition()})(?!)
你真的使用substr
和pos
?尤其像这样?我几乎从来没有这样做过。一部分原因是因为它对我怀有敌意,但主要原因是它很微妙。@tchrist在这种情况下,这似乎是最简单的方法pos
和substr
的优点是能够直截了当地理解vs带有look aheads和capture的正则表达式。我同意,pos
可以神秘地从你下面变出来,但小范围可以原谅许多罪恶。我的第一个冲动是一个非正则表达式的迭代解决方案,因为如果循环遍历字符,“匹配每一对字母”非常简单。我试着用捕获+前瞻的方式来实现它,但没能成功;我没有意识到你需要在《展望》中,而不是在它的周围,放上第二张照片。我读的越多,我就越觉得池上春树的作品更清晰。
#!/usr/bin/env perl
use strict;
use warnings;
my $in = "hello world.";
my @words = $in =~ /(\b\pL+\b)/g;
my @out = map {
my @chars = split '';
map { $chars[$_] . $chars[$_+1] } ( 0 .. $#chars - 1 );
} @words;
print join ',', @out;
print "\n";