Comment trouver la première occurrence d'un motif dans une chaîne à partir d'une position de départ?

StackOverflow https://stackoverflow.com/questions/120071

  •  02-07-2019
  •  | 
  •  

Question

J'ai une chaîne de longueur arbitraire et, à partir de la position p0, je dois trouver la première occurrence de l'un des trois modèles de 3 lettres.

Supposons que la chaîne ne contienne que des lettres. Je dois trouver le nombre de triplets commençant à la position p0 et sautant en avant jusqu'à la première occurrence de "aaa" ou "bbb" ou "ccc".

Est-ce même possible d'utiliser seulement une expression régulière?

Était-ce utile?

La solution

Moritz dit que cela pourrait être plus rapide qu’une regex. Même si c'est un peu plus lent, c'est plus facile à comprendre à 5 heures du matin. :)

             #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;
    }

Autres conseils

$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;

(En supposant que p0 soit basé sur 0).

Bien sûr, il est probablement plus efficace d’utiliser Substr sur la chaîne pour avancer:

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

Vous ne pouvez pas vraiment compter avec les regex, mais vous pouvez faire quelque chose comme ceci:

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;

Mais je pense qu'il est un peu plus rapide d'utiliser substr () et unpack () pour scinder en triple et faire marcher les triples dans une boucle for.

(edit: sa longueur (), pas sa longueur (); -)

La partie principale de ceci est divisée /(...)/. Mais à la fin de ceci, vous aurez vos positions et vos données d’occurrence.

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 { 

La partie principale de ceci est divisée /(...)/. Mais à la fin de ceci, vous aurez vos positions et vos données d’occurrence.

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

Ou pour un comptage simple par regex (il utilise Experimental (?? {}))

<*>, [] } @expected_triplets; foreach my $i ( 0..@triplets ) { my $triplet = $triplets[$i]; push( @{$occurrence_for{$triplet}}, $i ) if exists $occurrence_for{$triplet}; }

Ou pour un comptage simple par regex (il utilise Experimental (?? {}))

<*>

Si la rapidité est un problème sérieux, vous pouvez, en fonction de la nature des trois chaînes, devenir vraiment chic en créant un arbre (par exemple, l'algorithme Aho-Corasick ou similaire).

Une carte pour chaque état possible est possible, par exemple. state [0] ['a'] = 0 si aucune chaîne ne commence par 'a'.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top