perl 如何从某个起始位置找到字符串中第一个出现的模式?

gijlo24d  于 2023-03-03  发布在  Perl
关注(0)|答案(5)|浏览(203)

我有一个任意长度的字符串,从位置p0开始,我需要找到三个3字母模式中第一个出现的模式。
假设字符串只包含字母,我需要计算从位置p0开始的三个一组的计数,并以三个一组的形式向前跳转,直到第一次出现'aaa'或'bbb'或'ccc'。
仅仅使用一个正则表达式就能做到这一点吗?

niknxzdl

niknxzdl1#

$string=~/^   # from the start of the string
            (?:.{$p0}) # skip (don't capture) "$p0" occurrences of any character
            (?:...)*?  # skip 3 characters at a time,
                       # as few times as possible (non-greedy)
            (aaa|bbb|ccc) # capture aaa or bbb or ccc as $1
         /x;

(假设p0从0开始)。
当然,在字符串上使用substr向前跳可能更有效:

substr($string, $p0)=~/^(?:...)*?(aaa|bbb|ccc)/;
smdnsysy

smdnsysy2#

你不能真正用正则表达式来计数,但是你可以这样做:

pos $string = $start_from;
$string =~ m/\G         # anchor to previous pos()
            ((?:...)*?) # capture everything up to the match
            (aaa|bbb|ccc)
            /xs  or die "No match"
my $result = length($1) / 3;

但我认为使用substr()和unpack()拆分为三元组并在for循环中遍历三元组会更快一些。
(edit:它是长度(),而不是长度();-)

plicqrtu

plicqrtu3#

这个过程的主要部分是split /(...)/,但是在这个过程的最后,你会得到你的位置和发生数据。

my @expected_triplets = qw<aaa bbb ccc>;
my $data_string      
    = 'fjeidoaaaivtrxxcccfznaaauitbbbfzjasdjfncccftjtjqznnjgjaaajeitjgbbblafjan'
    ;
my $place          = 0;
my @triplets       = grep { length } split /(...)/, $data_string;
my %occurrence_for = map { $_, [] } @expected_triplets;
foreach my $i ( 0..@triplets ) {
    my $triplet = $triplets[$i];
    push( @{$occurrence_for{$triplet}}, $i ) if exists $occurrence_for{$triplet};
}

或者通过正则表达式进行简单计数(它使用Experimental(??{}))

my ( $count, %count );
my $data_string      
    = 'fjeidoaaaivtrxxcccfznaaauitbbbfzjasdjfncccftjtjqznnjgjaaajeitjgbbblafjan'
    ;
$data_string =~ m/(aaa|bbb|ccc)(??{ $count++; $count{$^N}++ })/g;
lo8azlld

lo8azlld4#

如果速度是一个严重的问题,你可以,取决于这3个字符串是什么,通过创建一个树(例如Aho-Corasick算法或类似)得到真正的花式。
每个可能状态的Map都是可能的,例如,如果没有字符串以'a'开始,则state[0]['a'] = 0。

gdx19jrr

gdx19jrr5#

莫里茨说这可能比正则表达式快,即使慢一点,在凌晨5点也更容易理解。

#0123456789.123456789.123456789.  
my $string = "alsdhfaaasccclaaaagalkfgblkgbklfs";  
my $pos    = 9;  
my $length = 3;  
my $regex  = qr/^(aaa|bbb|ccc)/;

while( $pos < length $string ) {  
    print "Checking $pos\n";  
    
    if( substr( $string, $pos, $length ) =~ /$regex/ ) {
        print "Found $1 at $pos\n";
        last;
        }

    $pos += $length;
    }

相关问题