web-dev-qa-db-de.com

Ersetzen Sie Wörter in einer unstrukturierten Textdatei mithilfe einer for-Schleife

Ich habe eine sehr unstrukturierte Textdatei, die ich mit readLines gelesen habe. Ich möchte bestimmte Zeichenfolgen in eine andere Zeichenfolge ändern, die sich in einer Variablen befindet (im Folgenden als "neu" bezeichnet). 

Im Folgenden soll der manipulierte Text alle Ausdrücke enthalten: "one", "two", "three" und "four" einmal anstelle der "change" -Strings. Wie Sie jedoch sehen können, ändert sich das erste Muster in jedem Element, aber ich brauche den Code, um zu ignorieren, dass es neue Zeichenfolgen mit Anführungszeichen gibt. 

Siehe Beispielcode und Daten unten. 

 #text to be changed
 text <- c("TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT change",
        "TEXT TEXT TEXT change TEXT TEXT TEXT TEXT TEXT change", 
        "TEXT TEXT TEXT change TEXT TEXT TEXT TEXT")

 #Variable containing input for text
 new <- c("one", "two", "three", "four")
 #For loop that I want to include 
 for (i in 1:length(new)) {

   text  <- sub(pattern = "change", replace = new[i], x = text)

 }
 text
9
Gorp

Wie wäre es damit? Die Logik ist, eine Zeichenkette so lange abzuhauen, bis sie keine change mehr hat. Bewegen Sie sich bei jedem "Treffer" (wo change gefunden wird) entlang des new-Vektors.

text <- c("TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT change",
          "TEXT TEXT TEXT change TEXT TEXT TEXT TEXT TEXT change", 
          "TEXT TEXT TEXT change TEXT TEXT TEXT TEXT")

#Variable containing input for text
new <- c("one", "two", "three", "four")
new.i <- 1

for (i in 1:length(text)) {
  while (grepl(pattern = "change", text[i])) {
    text[i] <- sub(pattern = "change", replacement = new[new.i], x = text[i])
    new.i <- new.i + 1
  }
}
text

[1] "TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT one" 
[2] "TEXT TEXT TEXT two TEXT TEXT TEXT TEXT TEXT three"
[3] "TEXT TEXT TEXT four TEXT TEXT TEXT TEXT" 
8
Roman Luštrik

Hier ist eine andere Lösung, die gregexpr() und regmatches() verwendet:

#text to be changed
text <- c("TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT change",
          "TEXT TEXT TEXT change TEXT TEXT TEXT TEXT TEXT change",
          "TEXT TEXT TEXT change TEXT TEXT TEXT TEXT")

#Variable containing input for text
new <- c("one", "two", "three", "four")

# Alter the structure of text
altered_text <- paste(text, collapse = "\n")

# So we can use gregexpr and regmatches to get what you want
matches <- gregexpr("change", altered_text)
regmatches(altered_text, matches) <- list(new)

# And here's the result
cat(altered_text)
#> TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT one
#> TEXT TEXT TEXT two TEXT TEXT TEXT TEXT TEXT three
#> TEXT TEXT TEXT four TEXT TEXT TEXT TEXT

# Or, putting the text back to its old structure
# (one element for each line)
unlist(strsplit(altered_text, "\n"))
#> [1] "TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT one" 
#> [2] "TEXT TEXT TEXT two TEXT TEXT TEXT TEXT TEXT three"
#> [3] "TEXT TEXT TEXT four TEXT TEXT TEXT TEXT"

Erstellt am 2018-10-16 vom reprex-Paket (v0.2.1)

Wir können dies tun, da gregexpr() alle Übereinstimmungen im Text für "change" finden kann; von help("gregexpr"):

regexpr gibt einen Ganzzahlvektor zurück, der die gleiche Länge hat wie text die Startposition des ersten Spiels ....

gregexpr gibt für jedes Element von .__ eine Liste mit derselben Länge wie text zurück. welches die gleiche Form hat wie der Rückgabewert für regexpr, außer dass Die Startpositionen von every (disjoint) werden angegeben.

(Betonung hinzugefügt).

Dann kann regmatches() verwendet werden, um entweder die gefundenen Übereinstimmungen mit gregexpr()zu extrahieren oder sie zu ersetzen; von help("regmatches"):

Verwendungszweck

regmatches (x, m, invertieren = FALSE)
regmatches (x, m, invertieren = FALSCH) <- Wert

...

wert
ein Objekt mit geeigneten Ersatzwerten für die Übereinstimmung oder nicht übereinstimmende Teilzeichenfolgen (siehe Details).

...

Einzelheiten

Die Ersetzungsfunktion kann zum Ersetzen des übereinstimmenden oder .__ verwendet werden. nicht übereinstimmende Teilzeichenfolgen. Für Vektorübereinstimmungsdaten, wenn invert FALSE ist, value sollte ein Zeichenvektor sein, dessen Länge der Anzahl der übereinstimmenden .__ entspricht. Elemente in m. Ansonsten sollte es eine Liste von Zeichenvektoren sein mit der gleichen Länge wie m, jeweils so lange die Anzahl der Ersetzungen erforderlich.

3
duckmayr

Ein anderer Ansatz, der strsplit verwendet:

tl <- lapply(text, function(s) strsplit(s, split = " ")[[1]])
df <- stack(setNames(tl, seq_along(tl)))

ix <- df$values == "change"
df[ix, "values"] <- new
tapply(df$values, df$ind, paste, collapse = " ")

was gibt:

                                                  1 
 "TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT one" 
                                                  2 
"TEXT TEXT TEXT two TEXT TEXT TEXT TEXT TEXT three" 
                                                  3 
          "TEXT TEXT TEXT four TEXT TEXT TEXT TEXT"

Zusätzlich können Sie den Aufruf tapply in unname einschließen:

 unname(tapply(df$values, df$ind, paste, collapse = " "))

was gibt:

[1] "TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT one" 
[2] "TEXT TEXT TEXT two TEXT TEXT TEXT TEXT TEXT three"
[3] "TEXT TEXT TEXT four TEXT TEXT TEXT TEXT"

Wenn Sie die Elemente von new nur einmal verwenden möchten, können Sie den Code folgendermaßen aktualisieren:

newnew <- new[1:3]

ix <- df$values == "change"
df[ix, "values"][1:length(newnew)] <- newnew
unname(tapply(df$values, df$ind, paste, collapse = " "))

Sie können dies weiter ändern, um auch die Situation zu berücksichtigen, in der es mehr Ersetzungen als Positionen gibt (Vorkommen des Musters, change im Beispiel), die ersetzt werden müssen:

newnew2 <- c(new, "five")

tl <- lapply(text, function(s) strsplit(s, split = " ")[[1]])
df <- stack(setNames(tl, seq_along(tl)))

ix <- df$values == "change"
df[ix, "values"][1:pmin(sum(ix),length(newnew2))] <- newnew2[1:pmin(sum(ix),length(newnew2))]
unname(tapply(df$values, df$ind, paste, collapse = " "))
1
Jaap