2014-12-11 10 views
10

Ich habe einen Datensatz wie folgt. Ich möchte alle Punkte zwischen zwei 1 durch 1 ersetzen, wie im gewünschten Ergebnis gezeigt. Kann ich das mit regex in Basis R tun?Wie Lücke zwischen zwei Zeichen mit Regex zu füllen

Ich habe versucht:

regexpr("^1\\.1$", my.data$my.string, perl = TRUE) 

Hier ist eine Lösung in C# ist

Characters between two exact characters

Sie für alle Anregungen danken.

my.data <- read.table(text=' 
    my.string       state 
    ................1...............1. A 
    ......1..........................1 A 
    .............1.....2.............. B 
    ......1.................1...2..... B 
    ....1....2........................ B 
    1...2............................. C 
    ..........1....................1.. C 
    .1............................1... C 
    .................1...........1.... C 
    ........1....2.................... C 
    ......1........................1.. C 
    ....1....1...2.................... D 
    ......1....................1...... D 
    .................1...2............ D 
', header = TRUE, na.strings = 'NA', stringsAsFactors = FALSE) 

desired.result <- read.table(text=' 
    my.string       state 
    ................11111111111111111. A 
    ......1111111111111111111111111111 A 
    .............1.....2.............. B 
    ......1111111111111111111...2..... B 
    ....1....2........................ B 
    1...2............................. C 
    ..........1111111111111111111111.. C 
    .111111111111111111111111111111... C 
    .................1111111111111.... C 
    ........1....2.................... C 
    ......11111111111111111111111111.. C 
    ....111111...2.................... D 
    ......1111111111111111111111...... D 
    .................1...2............ D 
', header = TRUE, na.strings = 'NA', stringsAsFactors = FALSE) 
+0

Das war das Muster, das würde habe mit 'regexpr' ein nützlicheres Ergebnis geliefert:' "1 \\. * 1" '. Die '^' und '$' würden nur Übereinstimmungen zulassen, wenn die 1en an beiden Enden der Zeichenkette wären (und das Fehlen eines Quantifizierers würde nur einer Zeichenkette mit nur einer Periode entsprechen.) –

+0

Was ist das erwartete Ergebnis, wenn vier (oder mehr) 1 sind auf der gleichen Linie (dh ".. 1 ... 1 ... 1 ... 1.")? '..11111 ... 11111..' oder' ..1111111111111..'? –

Antwort

12

Im Folgenden finden Sie eine Option gsub mit den \G Funktion und Lookarounds Behauptungen verwenden.

> gsub('(?:1|\\G(?<!^))\\K\\.(?=\\.*1)', '1', my.data$my.string, perl = TRUE) 
# [1] "................11111111111111111." "......1111111111111111111111111111" 
# [3] ".............1.....2.............." "......1111111111111111111...2....." 
# [5] "....1....2........................" "1...2............................." 
# [7] "..........1111111111111111111111.." ".111111111111111111111111111111..." 
# [9] ".................1111111111111...." "........1....2...................." 
# [11] "......11111111111111111111111111.." "....111111...2...................." 
# [13] "......1111111111111111111111......" ".................1...2............" 

Die \G Merkmal ist ein Anker, der in einer von zwei Positionen übereinstimmen kann; der Anfang der String-Position oder die Position am Ende des letzten Matches. Da Sie die Punkte am Anfang der Zeichenfolgenposition vermeiden möchten, verwenden wir eine Lookaround-Assertion \G(?<!^), um den Anfang der Zeichenfolge auszuschließen.

Die Escape-Sequenz \K setzt den Startpunkt der gemeldeten Übereinstimmung zurück und alle zuvor verbrauchten Zeichen sind nicht mehr enthalten.

Sie können eine allgemeine Aufschlüsselung finden, die den regulären Ausdruck here erklärt.

+1

Außergewöhnlich. Vielen Dank.Wenn Sie Zeit haben, überlegen Sie, wie das funktioniert. –

+6

Dies ist ein schlechter Arsch Regex. @MarkMiller, siehe [hier] (http://rick.measham.id.au/paste/explain.pl?regex=%28%3F%3A1%7C%5CG%28%3F%3C%21%5E%29 % 29% 5CK% 5C.% 28% 3F% 3D. *% 3F1% 29) –

+2

OMG! ein automatisierter Regex-Erklärer?!? –

5

Hier ist eine Option, die einen relativ einfachen regulären Ausdruck und die Standardkombination von gregexpr(), regmatches() und regmatches<-() zu identifizieren, Extrakt verwendet, betreiben, und dann, dass die Teilzeichen ersetzen regex entsprechen.

## Copy the character vector 
x <- my.data$my.string 
## Find sequences of "."s bracketed on either end by a "1" 
m <- gregexpr("(?<=1)\\.+(?=1)", x, perl=TRUE) 
## Standard template for operating on and replacing matched substrings 
regmatches(x,m) <- sapply(regmatches(x,m), function(X) gsub(".", "1", X)) 

## Check that it worked 
head(x) 
# [1] "................11111111111111111." "......1111111111111111111111111111" 
# [3] ".............1.....2.............." "......1111111111111111111...2....." 
# [5] "....1....2........................" "1...2............................." 
7

gsubfn Verwendung, das erste Argument ist ein regulärer Ausdruck, der die 1-en übereinstimmt und die Zeichen zwischen dem 1en und fängt den letzteren. Das zweite Argument ist eine Funktion, ausgedrückt in Formelschreibweise, die gsub verwendet mit 1 jedes Zeichen in den erfassten Zeichenfolge zu ersetzen:

library(gsubfn) 
transform(my.data, my.string = gsubfn("1(.*)1", ~ gsub(".", 1, x), my.string)) 

Wenn es dann "1(.*?)1" als regelmäßige Verwendung mehrere Paare von 1en in einer Zeichenfolge sein kann Ausdruck stattdessen.

Visualisierung Der reguläre Ausdruck ist hier einfach genug, dass es direkt hier verstanden werden kann, ist aber ein debuggex Visualisierung anwyays:

1(.*)1 

Regular expression visualization

Debuggex Demo

+1

Danke für das Hinzufügen. Ich hatte 'gsubfn()' eine schnelle Aufnahme gegeben (mit Lookahead- und Lookbehind-Assertions), wurde aber von seiner Nicht-PCRE-Regex-Engine behindert. Ich mag, wie sauber das die Lösung macht. –

Verwandte Themen