我需要帮助解决perl中的正则表达式问题。我需要在字母字符串(大小为1)周围匹配非字母字符“有核”。
也就是说......我有一个像
这样的字符串CDF((E)TR)FT
我希望匹配以下所有内容:
C, D, F((, ((E), )T, R), )F, T.
我正在尝试像
这样的东西/([^A-Za-z]*[A-Za-z]{1}[^A-Za-z]*)/
但我得到了:
C, D, F((, E), T, R), F, T.
就像是一旦匹配了非字母字符,就无法在另一个匹配中再次匹配。
我该怎么做?
答案 0 :(得分:4)
有点晚了。有人可能已经提出这个问题了。
我会将断言中的捕获消耗到左侧(通过backref),而不会在断言中使用捕获到右侧。可以看到所有捕获,但最后一个捕获没有消耗,所以下一个传递会在找到最后一个原子字母后继续。
为了清楚起见,简化了字符类:
/(?=([^A-Z]*))(\1[A-Z])(?=([^A-Z]*))/
(?=([^A-Z]*))
#ahead是可选的非A-Z字符,在grp 1中捕获
(\1[A-Z])
#capture grp 2,使用捕获组1,加上原子字母
(?=([^A-Z]*))
#ahead是可选的非A-Z字符,在grp 3中捕获
全局,在while循环中,组合组$2$3
(按此顺序)是答案。
测试:
$samp = 'CDF((E)TR)FT';
while ( $samp =~ /(?=([^A-Z]*))(\1[A-Z])(?=([^A-Z]*))/g )
{
print "$2$3, ";
}
输出:
C, D, F((, ((E), )T, R), )F, T,
答案 1 :(得分:2)
问题是您第一次遇到角色或非字母角色时就会消耗它们,因此无法匹配您想要的所有角色。解决方案是对不同的模式使用不同的正则表达式,并在最后组合结果,以便您可以获得所需的结果:
这将匹配所有字符,以非字符开头,后跟单个字符,但后面跟不是非字符
[^A-Z]+[A-Z](?![^A-Z])
这将匹配由非字符包围的字符,其中包含重叠结果:
(?=([^A-Z]+[A-Z][^A-Z]+))
仅当字符前面没有非字符时,才匹配一个字符后跟一个或多个非字符:
(?<![^A-Z])[A-Z][^A-Z]+
这将匹配未包含在非字符
中的单个字符(?<![^A-Z])[A-Z](?![^A-Z])
通过组合结果,您将获得正确的预期结果:
C,D,T, )T, )F, ((E), F((, R)
此外,如果您了解小部件,您可以将其加入一个正则表达式:
#!/usr/local/bin/perl
use strict;
my $subject = "0C0CC(R)CC(L)C0";
while ($subject =~ m/(?=([^A-Z]+[A-Z][^A-Z]+))|(?=((?<![^A-Z])[A-Z][^A-Z]+))|(?=((?<![^A-Z])[A-Z](?![^A-Z])))|(?=([^A-Z]+[A-Z](?![^A-Z])))/g) {
# matched text = $1, $2, $3, $4
print $1, " " if defined $1;
print $2, " " if defined $2;
print $3, " " if defined $3;
print $4, " " if defined $4;
}
输出:
0C0 0C C( (R) )C C( (L) )C0
答案 2 :(得分:1)
你是对的,一旦角色在正则表达式比赛中被消耗,它就无法再次匹配。在完全支持lookaround assertions的正则表达式中,您可以使用正则表达式
(?<=(\P{L}*))\p{L}(?=(\P{L}*))
匹配结果为字母,$1
和$2
将包含非字母。由于它们仅在外观断言的上下文中匹配,因此它们不会在匹配中使用,因此可以多次匹配。然后,您需要将匹配结果构造为$1 + $& + $2
。例如,这种方法适用于.NET。
在大多数其他风格(包括Perl)中,对于环视的支持有限,你可以采取混合方法,这是必要的,因为后瞻性表达不允许无限期重复:
\P{L}*\p{L}(?=(\P{L}*))
现在$&
将包含字母前面的非字母字符和字母本身,$1
包含字母后面的任何非字母字符。
while ($subject =~ m/\P{L}*\p{L}(?=(\P{L}*))/g) {
# matched text = $& . $1
}
答案 3 :(得分:1)
或者,您可以通过艰难的方式完成并首先进行标记,然后处理标记:
#!/usr/bin/perl
use warnings;
use strict;
my $str = 'CDF((E)TR)FT';
my @nucleated = nucleat($str);
print "$_\n" for @nucleated;
sub nucleat {
my($s) = @_;
my @parts; # return list stored here
my @tokens = grep length, split /([a-z])/i, $s;
# bracket the tokens with empty strings to avoid warnings
unshift @tokens, '';
push @tokens, '';
foreach my $i (0..$#tokens) {
next unless $tokens[$i] =~ /^[a-z]$/i; # one element per letter token
my $str = '';
if ($tokens[$i-1] !~ /^[a-z]$/i) { # punc before letter
$str .= $tokens[$i-1];
}
$str .= $tokens[$i]; # the letter
if ($tokens[$i+1] !~ /^[a-z]$/i) { # punc after letter
$str .= $tokens[$i+1];
}
push @parts, $str;
}
return @parts;
}