Wie kann ich das erste Vorkommen eines Musters in einer Zeichenfolge von einer Ausgangsposition zu finden?

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

  •  02-07-2019
  •  | 
  •  

Frage

Ich habe eine Reihe von beliebiger Länge und an der Position p0 beginnen, ich brauche das erste Vorkommen eines von drei 3-Buchstaben-Mustern zu finden.

Nehmen wir die Zeichenfolge nur Buchstaben enthalten. Ich brauche die Anzahl der Drillinge an Position p0 beginnen zu finden und nach vorn in Triolen bis zum ersten Auftreten von entweder ‚aaa‘ oder ‚bbb‘ oder ‚ccc‘ springen.

Ist dies überhaupt möglich mit nur einem regex?

War es hilfreich?

Lösung

Moritz sagt dies schneller als ein regex sein könnte. Auch wenn es ein wenig langsamer ist, ist es einfacher, um 5 Uhr morgens zu verstehen. :)

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

Andere Tipps

$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 Unter der Annahme 0-basiert).

Natürlich, es ist wahrscheinlich effizienter substr auf die Saite zu verwenden vorwärts zu überspringen:

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

Sie können wirklich nicht mit regulären Ausdrücken zählen, aber Sie können etwas tun:

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;

Aber ich denke, es ist ein bisschen schneller substr () zu verwenden, und entpacken () in dreifach aufgeteilt und die Tripel in einer for-Schleife gehen.

(edit: es ist length (), nicht Länge (); -)

Der Hauptteil davon wird geteilt /(...)/. Aber am Ende dieser, werden Sie Ihre Positionen und Daten über das Vorkommen haben.

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

oder für einfaches Zählen von regex (es verwendet Experimental (?? {}))

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

Wenn die Geschwindigkeit ein ernstes Problem ist, können Sie, je nachdem, was die drei Saiten sind, bekommt wirklich Lust durch einen Baum zu schaffen (zum Beispiel Aho-Corasick Algorithmus oder ähnliches).

Eine Karte für jeden möglichen Zustand ist möglich, beispielsweise Zustand [0] [ 'a'] = 0, wenn keine Strings beginnen mit 'a'.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top