Wie unterstützt das Look-ahead- und Look-behind-Konzept solche Zero-Width-Assertions in Regex of Ruby?

7

Ich habe gerade das Konzept Zero-Width Assertions aus der Dokumentation gelesen. Und einige kurze Fragen kommen mir in den Sinn -

  • warum solch ein Name Zero-Width Assertions ?
  • Wie das Look-ahead und look-behind Konzept solche unterstützt Zero-Width Assertions Konzept?
  • Was bedeuten diese ?<=s , <!s , =s , <=s - 4 Symbole innerhalb des Musters? Kannst du mir hier helfen, mich darauf zu konzentrieren, zu verstehen, was eigentlich los ist?

Ich habe auch ein paar winzige Codes ausprobiert, um die Logik zu verstehen, aber ich bin nicht so zuversichtlich, was die Ergebnisse angeht:

%Vor%

Kann mir hier jemand helfen, zu verstehen?

BEARBEITEN

Hier habe ich zwei Snippets ausprobiert, eines mit "Zero-Width Assertions" -Konzepten wie folgt:

%Vor%

und das andere ist ohne "Zero-Width Assertions" -Konzepte wie folgt:

%Vor%

Sowohl das obige erzeugt die gleiche Ausgabe, jetzt intern, wie die beiden regexp sich selbst bewegen, um die Ausgabe zu erzeugen - könntest du mir helfen, es zu visualisieren?

Danke

    
Arup Rakshit 17.01.2013, 20:34
quelle

3 Antworten

16

Reguläre Ausdrücke passen von links nach rechts und bewegen eine Art "Cursor" entlang der Zeichenfolge, während sie gehen. Wenn Ihre Regex ein normales Zeichen wie a enthält, heißt das: "Wenn vor dem Cursor ein Buchstabe a steht, bewegen Sie den Cursor ein Zeichen weiter und weiter. Sonst stimmt etwas nicht, sichern Sie und versuchen Sie etwas sonst." Man könnte also sagen, dass a eine "Breite" von einem Zeichen hat.

Eine "zero-width assertion" ist genau das: es behauptet etwas über die Zeichenkette (dh stimmt nicht überein, wenn eine Bedingung nicht erfüllt ist), verschiebt aber nicht die Cursor vorwärts, weil seine "Breite" Null ist.

Sie sind wahrscheinlich bereits mit einigen einfacheren Zusicherungen der Breite null vertraut, wie ^ und $ . Diese stimmen mit dem Anfang und dem Ende einer Zeichenfolge überein. Wenn der Cursor beim Anzeigen dieser Symbole nicht am Anfang oder Ende steht, wird die Regex-Engine fehlschlagen, sichern und etwas anderes versuchen. Sie bewegen den Cursor jedoch nicht vorwärts, da sie keine Zeichen enthalten. Sie überprüfen nur, wo der Cursor ist.

Lookahead und lookbehind arbeiten auf die gleiche Weise. Wenn die Regex-Engine versucht, sie abzugleichen, überprüft sie um den Cursor, um zu sehen, ob das rechte Muster davor oder dahinter liegt, aber im Falle einer Übereinstimmung wird der Cursor nicht bewegt.

Überlegen Sie:

%Vor%

Dies wird übereinstimmen! Die Regex-Engine geht so:

  1. Beginnen Sie am Anfang der Zeichenfolge: |foo .
  2. Der erste Teil der Regex ist (?=foo) . Das bedeutet: Nur Übereinstimmung, wenn foo nach dem Cursor erscheint. Macht es? Nun ja, also können wir fortfahren. Der Cursor bewegt sich jedoch nicht , da dies eine Breite von null ist. Wir haben immer noch |foo .
  3. Weiter ist f . Befindet sich ein f vor dem Cursor? Ja, fahren Sie fort und bewegen Sie den Mauszeiger über f : f|oo .
  4. Weiter ist o . Befindet sich ein o vor dem Cursor? Ja, fahren Sie fort und bewegen Sie den Mauszeiger über o : fo|o .
  5. Dasselbe nochmal, bringt uns zu foo| .
  6. Wir haben das Ende der Regex erreicht, und nichts ist fehlgeschlagen, daher stimmt das Muster überein.

Auf Ihre vier Behauptungen insbesondere:

  • (?=...) ist "Lookahead"; Es gibt an, dass ... nach dem Cursor angezeigt wird.

    %Vor%

    Das "ju" in "jump" passt zusammen, weil ein "m" als nächstes kommt. Aber das "ju" in "june" hat kein "m" als nächstes, also ist es in Ruhe gelassen.

    Da der Cursor nicht bewegt wird, müssen Sie vorsichtig sein, wenn Sie etwas nachstellen. (?=a)b wird niemals mit irgendetwas übereinstimmen, da es prüft, ob das nächste Zeichen a ist, dann überprüft auch , ob das gleiche Zeichen b ist, was nicht möglich ist.

  • (?<=...) ist "lookbehind"; Es gibt an, dass ... vor dem Cursor erscheint.

    %Vor%

    Das "unser" in "vier" passt, weil es ein "f" unmittelbar davor gibt, aber das "unser" in "mehl" hat ein "l" unmittelbar davor, also stimmt es nicht überein.

    Wie oben, müssen Sie vorsichtig sein mit dem, was Sie vor setzen. a(?<=b) wird niemals übereinstimmen, weil es prüft, ob das nächste Zeichen a ist, bewegt den Cursor und überprüft, ob das vorherige Zeichen b war.

  • (?!...) ist "negativer Lookahead"; es behauptet, dass ... nicht nach dem Cursor erscheint.

    %Vor%

    "Kind" stimmt überein, denn was als nächstes kommt, ist ein Leerzeichen, nicht "ren". "Kinder" nicht.

    Dies ist wahrscheinlich derjenige, den ich am meisten benutze; es ist sehr praktisch, fein zu kontrollieren, was nicht kommen kann.

  • (?<!...) ist "negativer Lookbehind"; Es gibt an, dass ... nicht vor dem Cursor angezeigt wird.

    %Vor%

    Das "oot" in "foot" ist in Ordnung, da es kein "r" davor gibt. Das "oot" in "root" hat eindeutig ein "r".

    Als zusätzliche Einschränkung verlangen die meisten Regex-Engines, dass ... in diesem Fall eine feste Länge hat. Sie können also ? , + , * oder {n,m} nicht verwenden.

Sie können diese auch verschachteln und alle möglichen verrückten Dinge tun. Ich benutze sie hauptsächlich für Einzelanfertigungen, von denen ich weiß, dass ich sie nie warten muss, daher habe ich keine großartigen Beispiele für praktische Anwendungen. Ehrlich gesagt, sie sind seltsam genug, dass du zuerst versuchen solltest, das zu tun, was du willst. :)

Nachträglich: Die Syntax kommt von regulären Perl-Ausdrücken , die (? verwenden, gefolgt von verschiedenen Symbolen für viele erweiterte Syntax, da ? alleine ungültig ist. Also bedeutet <= nichts für sich; (?<= ist ein vollständiges Token, was "das ist der Beginn eines Lookbehind" bedeutet. Es ist, als ob += und ++ separate Operatoren sind, obwohl beide mit + beginnen.

Sie sind jedoch leicht zu merken: = gibt an, vorwärts zu schauen (oder wirklich "hier"), < gibt an, rückwärts zu schauen, und ! hat die traditionelle Bedeutung von "nicht".

Zu Ihren späteren Beispielen:

%Vor%

Ja, diese erzeugen die gleiche Ausgabe. Das ist das knifflige bisschen mit Lookahead:

  1. Die Regex-Engine hat einige Dinge ausprobiert, aber sie haben nicht funktioniert, und jetzt ist es bei fores|ight .
  2. Es überprüft (?!s) . Ist das Zeichen nach der Cursor s ? Nein, es ist i ! Dieser Teil passt also und der Abgleich wird fortgesetzt, aber der Cursor verschiebt sich nicht und wir haben weiterhin fores|ight .
  3. Es überprüft ight . Ist ight nach dem Cursor? Nun, ja, tut es, also bewege den Cursor: foresight| .
  4. Wir sind fertig!

Der Cursor bewegte sich über den Teilstring ight , das ist also die vollständige Übereinstimmung, und das wird ersetzt.

Doing (?!a)b ist nutzlos, da du sagst: das nächste Zeichen darf nicht a sein, und es muss b sein. Aber das ist das gleiche wie nur b ! Zu finden!

Dies kann manchmal nützlich sein, aber Sie benötigen ein komplexeres Muster: Zum Beispiel passt (?!3)\d zu einer Ziffer, die keine 3 ist.

Das ist was du willst:

%Vor%

Dies besagt, dass s nicht vor ight kommt.

    
Eevee 17.01.2013, 21:00
quelle
5

Zero-width assertions sind schwer zu verstehen, bis Sie feststellen, dass regex Positionen sowie Zeichen entspricht.

Wenn Sie die Zeichenkette "foo" sehen, lesen Sie natürlich drei Zeichen. Aber es gibt auch vier Positionen , die hier durch Pipes gekennzeichnet sind: "| f | o | o |". Ein Lookahead oder Lookbehind (auch bekannt als Lookarounds) entsprechen einer Position, an der das Zeichen vor oder nach dem Ausdruck übereinstimmt.

Der Unterschied zwischen einem Ausdruck mit der Breite null und anderen Ausdrücken besteht darin, dass der Ausdruck mit der Breite null nur mit der Position übereinstimmt (oder "konsumiert"). Also zum Beispiel:

%Vor%

stimmt nicht mit "apple" überein, weil versucht wird, "app" zweimal zu finden. Aber

%Vor%

wird erfolgreich sein, da der Lookahead nur der Position entspricht, auf die "app" folgt. Es stimmt nicht mit dem "app" -Zeichen überein, sodass der nächste Ausdruck sie konsumieren kann.

LOOKAROUND BESCHREIBUNGEN

  

Positiver Lookahead: (?=s)

     
    

Stellen Sie sich vor, Sie sind ein Feldwebel und führen eine Inspektion durch. Sie beginnen am Anfang der Linie mit der Absicht, an jedem privaten vorbeizugehen und sicherzustellen, dass sie die Erwartungen erfüllen. Aber bevor Sie das tun, schauen Sie einzeln nach vorn, um sicherzugehen, dass sie sich in der Eigentumsordnung aufgereiht haben. Die Namen der Privatpersonen sind "A", "B", "C", "D" und "E". %Code%. Ja, sie sind alle anwesend und verantwortlich.

  
     

Negative Vorausschau: /(?=ABCDE)...../.match('ABCDE')

     
    

Du führst die Inspektion auf der ganzen Linie durch und stehst schließlich auf Privat D. Jetzt wirst du nach vorne schauen, um sicherzustellen, dass "F" von der anderen Firma nicht versehentlich wieder in die falsche Formation gerutscht ist. %Code%. Nein, er ist in dieser Zeit nicht ausgerutscht, also ist alles in Ordnung.

  
     

Positiver Lookbehind: (?!s)

     
    

Nach Abschluss der Inspektion steht der Sergeant am Ende der Formation. Er dreht sich um und scannt zurück, um sicherzugehen, dass sich niemand davon entfernt hat. %Code%. Ja, jeder ist anwesend und verantwortlich.

  
     

Negatives Lookbehind: /.....(?!F)/.match('ABCDE')

     
    

Schließlich nimmt der Feldwebel noch einen letzten Blick, um sicherzustellen, dass die Soldaten A und B nicht noch einmal die Plätze gewechselt haben (weil sie KP mögen). %Code%. Nein, haben sie nicht, also ist alles gut.

  
    
JDB 17.01.2013 21:00
quelle
2

Die Bedeutung einer Zero-Weiten-Assertion ist ein Ausdruck, der bei der Übereinstimmung null Zeichen verbraucht. In diesem Beispiel beispielsweise

%Vor%

Was passt, ist

%Vor%

und somit wäre das Ergebnis

%Vor%

In diesem Beispiel jedoch

%Vor%

Was passt, ist

%Vor%

und daher wäre das Ergebnis

%Vor%

Ein weiteres Beispiel für eine Assertion mit der Breite null ist das Wortgrenze Zeichen, \b . Um beispielsweise ein vollständiges Wort zu finden, können Sie versuchen, das Wort mit Leerzeichen zu umgeben, z. B.

%Vor%

um

zu erhalten %Vor%

Aber sehen Sie, wie die Übereinstimmung der Räume es während der Substitution entfernt? Mit einer Wortgrenze umgehen Sie dieses Problem:

%Vor%

\b entspricht dem Anfang oder Ende eines Wortes, entspricht jedoch keinem Zeichen: es ist null-width .

Vielleicht lautet die knappste Antwort auf Ihre Frage: Lookahead- und lookbehind-Assertions sind eine Art von Assertionen mit der Breite null. Alle Lookahead- und Lookbehind-Assertionen sind Assertionen mit der Breite null.

Hier sind Erläuterungen zu Ihren Beispielen:

%Vor%

Oben heißt es: "Entspricht dem nächsten Zeichen, nicht einem s und dann einem i ." Dies ist immer true für i , da ein i niemals ein s ist, also ist die Substitution erfolgreich.

%Vor%

Oben sagt man: "Entspricht dem nächsten Zeichen ist ein s und dann ein i ." Dies ist nie wahr, da ein i niemals ein s ist, daher schlägt die Substitution fehl.

%Vor%

Oben, bereits erklärt. (Dies ist der richtige.)

%Vor%

Oben, sollte jetzt klar sein. In diesem Fall würde "Feuergefecht" anstelle von "Feuer", aber nicht "Voraussicht", "vorhersehen".

    
Andrew Cheong 17.01.2013 20:51
quelle

Tags und Links