帮助perl正则表达式规则

时间:2011-09-25 10:50:19

标签: regex perl

我需要帮助解决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.

就像是一旦匹配了非字母字符,就无法在另一个匹配中再次匹配。

我该怎么做?

4 个答案:

答案 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;
}