Warum verwendet strsplit positive Lookahead- und Lookbehind-Assertion-Übereinstimmungen?

Gesunder Menschenverstand und eine gregexpr() mit gregexpr() zeigen an, dass die Look- gregexpr() und Look-Ahead-Assertion unten in testString jeweils an genau einer Stelle testString :

 testString <- "text XX text" BB <- "(?<= XX )" FF <- "(?= XX )" as.vector(gregexpr(BB, testString, perl=TRUE)[[1]]) # [1] 9 as.vector(gregexpr(FF, testString, perl=TRUE)[[1]][1]) # [1] 5 

strsplit() verwendet diese Übereinstimmungsstellen jedoch anders und spaltet testString an einer Stelle, wenn die Lookbehind-Assertion verwendet wird, aber an zwei Stellen – die zweite scheint falsch zu sein -, wenn die Lookahead-Zusicherung verwendet wird.

 strsplit(testString, BB, perl=TRUE) # [[1]] # [1] "text XX " "text" strsplit(testString, FF, perl=TRUE) # [[1]] # [1] "text" " " "XX text" 

Ich habe zwei Fragen: (Q1) Was ist hier los? Und (Q2) wie kann man sich strsplit() besser strsplit() ?


Update: Theodore Lytras ‘ausgezeichnete Antwort erklärt, was vor sich geht, und spricht so (Q1) . Meine Antwort baut auf seiner auf, um ein Heilmittel zu finden, das sich anspricht (Q2) .

Solutions Collecting From Web of "Warum verwendet strsplit positive Lookahead- und Lookbehind-Assertion-Übereinstimmungen?"

Ich bin mir nicht sicher, ob dies ein Fehler ist, weil ich glaube, dass dies auf der Grundlage der R-Dokumentation erwartet wird. Von ?strsplit :

Der Algorithmus, der auf jede Eingabezeichenfolge angewendet wird, ist

 repeat { if the string is empty break. if there is a match add the string to the left of the match to the output. remove the match and all to the left of it. else add the string to the output. break. } 

Beachten Sie, dass dies bedeutet, dass das erste Element der Ausgabe “” “ist, wenn am Anfang einer (nicht leeren) Zeichenfolge eine Übereinstimmung vorhanden ist. Liegt jedoch eine Übereinstimmung am Ende der Zeichenfolge vor, lautet die Ausgabe Das gleiche wie bei dem Spiel entfernt.

Das Problem besteht darin, dass Lookahead (und Lookbehind) Assertions null Länge sind. Also zum Beispiel in diesem Fall:

 FF < - "(?=funky)" testString <- "take me to funky town" gregexpr(FF,testString,perl=TRUE) # [[1]] # [1] 12 # attr(,"match.length") # [1] 0 # attr(,"useBytes") # [1] TRUE strsplit(testString,FF,perl=TRUE) # [[1]] # [1] "take me to " "f" "unky town" 

Was passiert, ist, dass der einsame Lookahead (?=funky) auf Position 12 übereinstimmt. Der erste Split enthält also die Saite bis Position 11 (links vom Match), und sie wird zusammen mit dem Match aus der Saite entfernt. hat jedoch keine Länge.

Jetzt ist die verbleibende Zeichenfolge eine funky town , und die Lookahead stimmt mit der Position 1 überein. Es gibt jedoch nichts zu entfernen, da sich auf der linken Seite des Matches nichts befindet und die Übereinstimmung selbst null Länge hat. Also steckt der Algorithmus in einer Endlosschleife. Anscheinend triggers R das auf, indem er ein einzelnes Zeichen aufspaltet, was übrigens das dokumentierte Verhalten ist, wenn strsplit mit einer leeren Regex split="" (wenn argument split="" ). Danach ist die verbleibende Zeichenkette unky town , die als letzte Spalte zurückgegeben wird, da keine Übereinstimmung unky town .

Lookbehinds sind kein Problem, da jede Übereinstimmung geteilt und von der verbleibenden Zeichenfolge entfernt wird, so dass der Algorithmus nie hängen bleibt.

Zugegeben, dieses Verhalten sieht auf den ersten Blick seltsam aus. Ein anderes Verhalten würde jedoch die Annahme der Nulllänge für Lookaheads verletzen. Da der strsplit Algorithmus dokumentiert ist, glaube ich, dass dies nicht der Definition eines Fehlers entspricht.

Basierend auf der sorgfältigen Erläuterung des Verhaltens von substr() Theodore Lytras besteht eine relativ saubere Problemumgehung darin, der Lookahead-Assertion, die passend sein soll, eine positive Lookbehind-Assertion voran zu geben, die mit jedem einzelnen Zeichen übereinstimmt:

 testString < - "take me to funky town" FF2 <- "(?<=.)(?=funky)" strsplit(testString, FF2, perl=TRUE) # [[1]] # [1] "take me to " "funky town" 

Sieht für mich wie ein Käfer aus. Dies scheint nicht nur auf Räume bezogen zu sein, sondern eher auf einsame Lookahead (positiv oder negativ):

 FF < - "(?=funky)" testString <- "take me to funky town" strsplit(testString,FF,perl=TRUE) # [[1]] # [1] "take me to " "f" "unky town" FF <- "(?=funky)" testString <- "funky take me to funky funky town" strsplit(testString,FF,perl=TRUE) # [[1]] # [1] "f" "unky take me to " "f" "unky " # [5] "f" "unky town" FF <- "(?!y)" testString <- "xxxyxxxxxxx" strsplit(testString,FF,perl=TRUE) # [[1]] # [1] "xxx" "y" "xxxxxxx" 

Scheint gut zu funktionieren, wenn etwas gegeben wird, das zusammen mit der Assertion null Breite erfasst wird, wie:

 FF < - " (?=XX )" testString <- "text XX text" strsplit(testString,FF,perl=TRUE) # [[1]] # [1] "text" "XX text" FF <- "(?= XX ) " testString <- "text XX text" strsplit(testString,FF,perl=TRUE) # [[1]] # [1] "text" "XX text" 

Vielleicht könnte so etwas als Workaround dienen.