Wie kann ich überlappende Teilstrings in Perl zählen?

7

Ich muss ein Programm implementieren, um das Auftreten einer Teilzeichenkette in einer Zeichenkette in Perl zu zählen. Ich habe es wie folgt implementiert:

%Vor%

Das würde ich normalerweise tun. In der obigen Implementierung möchte ich jedoch das Auftreten von 'aa' in 'aaa' zählen. hier bekomme ich Antwort als 1, was vernünftig erscheint, aber ich muss auch die überlappenden Fälle berücksichtigen. daher sollte der obige Fall eine Antwort als 2 geben, da es zwei "aa" gibt, wenn wir Überschneidungen berücksichtigen.

kann jemand vorschlagen, wie eine solche Funktion zu implementieren?

    
sfactor 22.01.2010, 00:51
quelle

6 Antworten

8

Siehe ysths Antwort ... das habe ich nicht erkannt Das Muster könnte nur aus einer Assertion mit einer Breite von null bestehen und trotzdem für diesen Zweck arbeiten.

Sie können das positive Lookahead wie von anderen vorgeschlagen verwenden und die Funktion als schreiben :

%Vor%

Sie können auch pos verwenden, um festzulegen, wo die nächste Suche beginnt:

%Vor%

Ausgabe:

%Vor%     
Sinan Ünür 22.01.2010, 01:01
quelle
12

Jeder wird ziemlich kompliziert in seinen Antworten (d'oh! daotoad hätte seinen Kommentar eine Antwort geben sollen!), vielleicht weil sie Angst vor dem Ziegenbetreiber haben. Ich habe es nicht genannt, so nennen es die Leute. Es verwendet den Trick, dass das Ergebnis einer Listenzuweisung die Anzahl der Elemente in der rechten Liste ist.

Das Perl-Idiom zum Zählen von Matches ist dann:

%Vor%

Der Goatse-Teil ist die = () = , eine leere Liste mitten in zwei Zuweisungen. Der linke Teil der Ziege erhält die Zählung von der rechten Seite der Ziege. Beachten Sie, dass Sie eine Aufnahme im Muster benötigen, da dies die Liste ist, die der Übereinstimmungsoperator im Listenkontext zurückgibt.

Nun, der nächste Trick in Ihrem Fall ist, dass Sie wirklich einen positiven Lookbehind (oder Lookahead vielleicht) wollen. Die Lookarounds konsumieren keine Zeichen, sodass Sie die Position nicht verfolgen müssen:

%Vor%

Ihr aaa ist nur ein Beispiel. Wenn Sie ein Muster mit variabler Breite haben, müssen Sie ein Lookahead verwenden. Lookbehinds in Perl müssen fest eingestellt sein.

    
brian d foy 22.01.2010 01:21
quelle
8
%Vor%

Einige Punkte:

//g im Listenkontext stimmt so oft wie möglich überein.

\Q...\E wird verwendet, um alle Metazeichen automatisch zu entfernen, so dass Sie eine Anzahl von Teilstrings und keine Unterpatterns zählen.

Die Verwendung eines Lookaheads (?= ... ) führt dazu, dass jede Übereinstimmung die Zeichenfolge nicht "konsumiert", sodass die folgende Übereinstimmung beim nächsten Zeichen versucht werden kann.

Dieselbe Funktion wird verwendet, wenn eine Listenzuweisung (in diesem Fall an eine leere Liste) im skalaren Kontext die Anzahl der Elemente auf der rechten Seite der Listenzuweisung als Ziege / fliegende Linse / Spread-Adler / was auch immer zurückgibt operator, verwendet jedoch skalar () anstelle einer skalaren Zuweisung, um den skalaren Kontext bereitzustellen.

$_[0] wird nicht direkt verwendet, sondern stattdessen in ein Lexikon kopiert; Eine naive Verwendung von $_[0] anstelle von $string würde dazu führen, dass //g zu Beginn des Strings beginnt, wenn der übergebene String ein gespeichertes pos() hat.

Update: s /// g ist schneller, aber nicht so schnell wie mit index:

%Vor%     
ysth 22.01.2010 09:48
quelle
3

Sie könnten eine Lookahead-Bestätigung im regulären Ausdruck verwenden:

%Vor%

Ich vermute jedoch, dass Sinans Vorschlag schneller sein wird.

    
martin clayton 22.01.2010 01:10
quelle
3

Sie können dies versuchen, nicht mehr Regex als nötig.

%Vor%

Ausgabe

%Vor%     
ghostdog74 22.01.2010 01:13
quelle
3

Wenn die Geschwindigkeit ein Problem ist, ist der Ansatz index , der von ghostdog74 (mit der Verbesserung von cjm) vorgeschlagen wird, wahrscheinlich wesentlich schneller als die Regex-Lösungen.

%Vor%     
FMc 22.01.2010 14:01
quelle

Tags und Links