我正在寻找一个行为如下的正则表达式:
输入:“你好世界。”
输出:he, el, ll, lo, wo, or, rl, ld
我的想法类似于
while($string =~ m/(([a-zA-Z])([a-zA-Z]))/g) {
print "$1-$2 ";
}
但这有点不同。
我正在寻找一个行为如下的正则表达式:
输入:“你好世界。”
输出:he, el, ll, lo, wo, or, 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 $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);
产生这个:
Found 8 matches: 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);
这会产生与以前相同的输出。
但你可能还是会头疼。
无需“强制回溯”!
push @pairs, "$1$2" while /([a-zA-Z])(?=([a-zA-Z]))/g;
尽管您可能想要匹配任何字母而不是您指定的有限集。
push @pairs, "$1$2" while /(\pL)(?=(\pL))/g;
还有另一种方法。不使用任何正则表达式魔法,它确实使用了嵌套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 更具可读性。
您可以通过查找字母并使用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
正则表达式的位置。这有点紧凑,但零宽度断言可能会变得棘手。
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,特别是如果您不是正则表达式大师。