Eine Zeichenkette ist ein Stück Text zwischen Anführungszeichen.
mystring <- "some text"
mystring
#> [1] "some text"
typeof(mystring)
#> [1] "character"
Leider kann die Terminologie für Programmierer, die andere Sprachen kennen, verwirrend sein.
Obwohl der type character ist, macht R keinen Unterschied zwischen einem einzelnen Buchstaben (in einigen anderen Sprachen char genannt, in R aber nicht) und einer langen Zeichenkette.
Beide sind ein einzelnes Element und können wahlweise in einfachen ' '- oder doppelten " "-Anführungszeichen stehen.
Der Styleguide empfiehlt doppelte Anführungszeichen, außer wenn du innerhalb der Zeichenkette doppelte Anführungszeichen verwenden willst, ohne sie zu escapen.
str2 <- 'String with "quotes"' # convenience format
str2
#> [1] "String with \"quotes\"" # standard format
Ein character vector ist also ein Vektor aus Zeichenketten, wobei jede Zeichenkette ein einzelnes Element ist.
s <- c("some", "strings")
s
#> [1] "some" "strings"
length(s)
#> [1] 2
length(s[1]) # the number of vector elements, not the string length
#> [1] 1
nchar(s[1]) # the number of characters in the string "some"
#> [1] 4
Im obigen Beispiel ist length(s[1]) gleichwertig zu length("some"): die Anzahl der Vektorelemente, nicht die Länge der Zeichenkette.
Eine einzelne Zeichenkette zu bearbeiten ist natürlich möglich, wie nchar() im obigen Beispiel zeigt, erfordert aber bestimmte Funktionen, die weiter unten besprochen werden.
Wie wir oben gesehen haben, müssen einige Satzzeichen „escaped“ werden, indem man ihnen einen Backslash \ voranstellt.
Die Liste wird die meisten erfahrenen Programmierer nicht überraschen, denn dieses Thema ist sprachübergreifend ziemlich einheitlich standardisiert.
Neben Anführungszeichen (wie oben gezeigt) umfasst die Liste Zeilenumbrüche \n, Tabs \t, den Backslash \\ und Unicode-Symbole, die mit \U oder \u beginnen.
Um viele Escape-Zeichen zu vermeiden, kannst du Raw-Strings innerhalb von r"( )" definieren:
r"(raw "string")"
#> [1] "raw \"string\""
Für Raw-Strings, die Klammern ( ) enthalten, gibt es viele weitere Syntaxoptionen.
Jede aktuell unterstützte Version von R kodiert Zeichenketten in UTF-8, daher werden die meisten Sprachen der Welt unterstützt.
"आधुनिक मानक हिन्दी"
#> [1] "आधुनिक मानक हिन्दी"
Außerdem kennen viele R-Funktionen die locale und versuchen, lokale Konventionen für das Sortieren von Zeichenketten, die Umwandlung in Groß- und Kleinschreibung, Datum und Uhrzeit usw. zu befolgen.
Dieser Lehrplan zeigt Beispiele hauptsächlich für US-Englisch: zum einen, weil das ein verbreiteter Standard ist, und zum anderen, weil die Autoren des Lehrplans zufällig in den USA leben.
Locales sind ein komplexes Thema, daher suche bitte nach Dokumentation für deine eigene Situation.
In base R ist fast alles möglich, aber viele Programmierer finden die Unterstützung für die Bearbeitung von Zeichenketten ziemlich begrenzt und verwirrend.
Zum Glück gibt es tidyverse-Pakete, die viel intuitiver, flexibler und in sich stimmiger sind.
Es gibt noch viel Legacy-Code, der Zeichenkettenfunktionen im alten Stil verwendet. Dazu gehören die meisten Community-Lösungen auf Exercism.
Außerdem kann es bei kleinen Aufgaben nützlich sein, eingebaute Funktionen zu verwenden, statt eine große Bibliothek zu importieren. Die Maintainer des R-Tracks verfolgen diesen Ansatz oft, wenn sie Musterlösungen schreiben, um die Übungen in unserem GitHub-Repo zu validieren.
Daher lohnt es sich, ein wenig über die alten Ansätze zu wissen.
Die folgenden Funktionen werden in ihrer einfachsten Form vorgestellt, aber die meisten haben auch optionale Argumente. Details findest du in der Dokumentation.
ncharGibt die Anzahl der Zeichen in einer Zeichenkette zurück.
nchar("string")
#> [1] 6
nchar("Hrōðgār") # a person in Beowulf, written in Old English
#> [1] 7
Das (komplizierte!) Thema, was „Zeichen“ bedeutet, sprengt unseren Rahmen. Merke dir nur ein paar Punkte aus den obigen Beispielen:
\0-terminiert (was die Länge um 1 erhöhen würde).Sei vorsichtig, was du an nchar übergibst.
Bei Nicht-character-Werten können die Ergebnisse immer noch unvorhersehbar sein, obwohl sich das in neueren R-Versionen gebessert hat.
grepSucht ein Muster in einem Vektor aus Zeichenketten und gibt die Indizes mit einer Übereinstimmung zurück.
grep("Jav", c("Java", "Python", "Javascript"))
#> [1] 1 3
paste und paste0
Fügt eine beliebige Anzahl von Zeichenketten zu einer einzigen Zeichenkette zusammen.
Optional kann für paste ein Trennzeichen angegeben werden: Standard ist ein Leerzeichen.
paste0(v) ist gleichwertig zu paste(v, sep = "").
paste("bits", "of", "a", "string")
#> [1] "bits of a string"
paste0("bits", "of", "a", "string")
#> [1] "bitsofastring"
sprintfFür eine feinere Kontrolle beim Zusammensetzen von Zeichenketten übernimmt R die sprintf-Funktion aus C (und aus mehreren späteren Sprachen: Wikipedia listet etwa 30 auf).
r <- 5.3
pi <- 3.14159
sprintf("A circle of radius %.1f has area %.2f", r, pi * r^2)
#> [1] "A circle of radius 5.3 has area 88.25"
trimwsFührende und/oder nachfolgende Leerzeichen lassen sich mit trimws() entfernen:
s <- " messy string "
trimws(s)
#> [1] "messy string"
Es gibt einen Parameter which = "left" (oder "right"), um zu vermeiden, dass beide Enden getrimmt werden.
substrArbeitet auf einer einzelnen Zeichenkette und gibt eine Teilzeichenkette zwischen zwei (einschließlich) Grenzen zurück.
substr("Exercism", 5, 7)
#> [1] "cis"
strsplitTeilt eine Zeichenkette anhand einer Trennzeichenkette in eine R-Liste auf. Das Trennzeichen ist erforderlich (es gibt keinen Standardwert), kann aber eine leere Zeichenkette sein.
strsplit("R, Python, Julia", ", ")
[[1]]
#> [1] "R" "Python" "Julia"
strsplit("Exercism", "")
[[1]]
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"
Listen werden in einem separaten Konzept behandelt.
Wenn du diesen Teil des Lehrplans noch nicht erreicht hast, merke dir vorerst nur, dass die Funktion unlist() (in diesem Fall) eine Liste in einen Vektor umwandelt.
unlist(strsplit("Exercism", ""))
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"
regexpr und regexpr
Kurz für „regular expression“: Diese Funktionen suchen nach Vorkommen eines Musters in einer Zeichenkette und geben eine Liste mit (potenziell) nützlichen Daten zurück. Sie werden nur der Vollständigkeit halber erwähnt: es gibt bessere Optionen!
regexpr gibt nur das erste Vorkommen zurück, gregexpr gibt alle Vorkommen zurück („g“ steht für „global“ search).
Wie die Namen vermuten lassen, kann das Muster ein Regex sein, der in einem späteren Konzept behandelt wird.
Warum sehen diese alten Zeichenkettenfunktionen wie Unix-Shell-Befehle aus?
R entstand zwar 1993, wurde aber so entworfen, dass es weitgehend abwärtskompatibel zur Sprache S ist.
S wurde bei Bell Labs entwickelt und erschien erstmals 1976. Bell Labs war auch für Unix (ab 1969) und die Sprache C (1972) verantwortlich.
Was für ein Team aus dieser viel früheren Ära! Wir können nur vermuten, dass sie miteinander geredet und Ideen ausgetauscht haben.
Wohlwollend formuliert könnten wir sagen, dass die Zeichenkettenfunktionen im alten Stil nicht das Beste sind, was R zu bieten hat (weniger wohlwollende Ansichten sind ebenfalls verfügbar).
Um die Lage zu verbessern, wird die Bibliothek stringr seit 2009 entwickelt und ist heute ein zentraler Teil der Tidyverse-Sammlung.
Binde sie mit library(stringr) oder library(tidyverse) in deinen Namensraum ein.
Einige wichtige Merkmale:
str_ und versuchen, den Zweck klar zu machen.Wie alle Tidyverse-Pakete hat stringr eine gute Dokumentation, die meist von den Autorinnen und Autoren des Pakets geschrieben wurde.
In stringr gibt es Dutzende Funktionen, und sie haben oft eine Vielzahl optionaler Argumente.
Das kann anfangs etwas überwältigend sein, deshalb ist die Dokumentation dein Freund.
Überlege, was du hineingeben willst (eine einzelne Zeichenkette? einen Vektor? etwas Komplexeres?) und was du herausbekommen willst.
Wahrscheinlich gibt es einen einfachen Weg, das zu tun, was du willst, wenn du die richtige Funktion und die richtigen Argumente findest.
Die Verwendung von Pipes |> zum Verbinden von Funktionen wird an anderen Stellen des Lehrplans ausführlicher besprochen.
Vorerst ist es nützlich zu wissen, dass:
Das folgende Beispiel veranschaulicht die Syntax. Die einzelnen Funktionen werden später besprochen.
library(stringr)
"Monday-25-February" |>
str_split("-") |>
unlist() |> # [1] "Monday" "25" "February"
str_sub(1, 3) |> # [1] "Mon" "25" "Feb"
str_to_upper() |> # [1] "MON" "25" "FEB"
str_flatten(collapse = ", ")
#> [1] "MON, 25, FEB"
str_length zählt Codepoints (ähnlich wie nchars).
Verschiedene andere Funktionen bearbeiten Leerzeichen, darunter str_trim() und str_squish().
Das ist oft ein wichtiger Aufräumschritt am Anfang jedes Data-Science-Projekts.
# The first 3 kernels in JuPyteR notebooks:
str_length(c("Julia", "Python", "R")) #> [1] 1 6 5
#> [1] 5 6 1
str_pad(c("Julia", "Python", "R"), 8, "right") # add spaces if necessary
#> [1] "Julia " "Python " "R "
str_trim("Julia Python R ") # remove leading & trailing whitespace
#> [1] "Julia Python R"
str_squish("Julia Python R ") # trim, collapse multiple spaces
#> [1] "Julia Python R"
Die Funktion str_sub(string, start, end) gibt die Teilzeichenkette von start bis end (einschließlich) zurück, während str_sub_all() auf mehrere Zeichenketten gleichzeitig angewendet werden kann.
Das ist sehr flexibel.
start und end stehen standardmäßig für das erste bzw. letzte Zeichen.
Sie können Vektoren von Indizes sein.
In erfrischendem Gegensatz zur Vektorindizierung zählen negative Werte vom Ende der Zeichenkette aus rückwärts, wie in Python.
s <- "abcdefgh"
str_sub(s, 2, 4)
#> [1] "bcd"
str_sub(s, 5)
#> [1] "efgh"
str_sub(s, -3, -2)
#> [1] "fg"
str_sub(s, c(1, 4), c(2, 5))
#> [1] "ab" "de"
str_sub(s, c(1, 4, 6), c(2, 5, -1))
#> [1] "ab" "de" "fgh"
s |> str_sub(2, 4) # these functions are designed for pipes
#> [1] "bcd"
# str_sub_all takes vector input, gives list output
str_sub_all(c(s, "ijklmnop"), c(1, 4, 6), c(2, 5, -1))
[[1]]
#> [1] "ab" "de" "fgh"
[[2]]
#> [1] "ij" "lm" "nop"
str_flatten wandelt einen Zeichenvektor in eine einzelne Zeichenkette um.
Das Argument collapse wird zwischen die einzelnen Elemente eingefügt und hat den Standardwert "".
c("R", "Julia", "Python") |> str_flatten()
#> [1] "RJuliaPython"
c("R", "Julia", "Python") |> str_flatten(collapse = " - ")
#> [1] "R - Julia - Python"
str_c wandelt mehrere Zeichenvektoren in einen einzelnen Zeichenvektor um, wobei bei Bedarf recycelt wird.
str_c(LETTERS[1:8], 1:8, sep = ":")
#> [1] "A:1" "B:2" "C:3" "D:4" "E:5" "F:6" "G:7" "H:8"
str_glue bringt einfache Zeichenketten-Interpolation nach R.
Ausdrücke in geschweiften Klammern { } werden ausgewertet und ihre Textdarstellung wird eingesetzt.
Wenn mehrere Ausdrucks-Zeichenketten angegeben werden, werden sie ebenfalls wie bei str_flatten zusammengefügt.
Zeichenketten können optional auch von führenden bzw. nachfolgenden Leerzeichen befreit werden.
r <- 5.3
pi <- 3.14159
str_glue("A circle of radius {r} has area {pi * r^2}")
A circle of radius 5.3 has area 88.2472631
str_glue ist zwar flexibel (und ziemlich kompliziert), aber noch mehr Möglichkeiten bietet die zugrunde liegende Bibliothek glue.
Eine sehr häufige Verwendung von R ist, unordentliche Daten für die Analyse in ein einheitliches Format zu bringen. Dafür gibt es mehrere Möglichkeiten, Text in Teilzeichenketten aufzuteilen.
str_split_1 ist am einfachsten: Sie wandelt eine einzelne Zeichenkette in einen Vektor aus Teilstücken um, aufgeteilt an einem angegebenen Muster (es gibt kein Standardmuster).
s <- "R Julia Python"
str_split_1(s, " ")
#> [1] "R" "Julia" "Python"
str_split_i nimmt einen Zeichenvektor und einen Index n und gibt einen Vektor der n-ten Teilzeichenkette jeder Eingabe-Zeichenkette zurück.
s2 <- c(s, "C Kotlin, F#") #> [1] "R Julia Python" "C Kotlin, F#"
str_split_i(s2, " ", 2) # 2nd element of each
#> [1] "Julia" "Kotlin,"
str_split nimmt einen Zeichenvektor und gibt eine Liste von Vektoren zurück.
In unserem Lehrplan gibt es ein Listen-Konzept, das die Ausgabe erklärt.
str_split(s2, " ")
[[1]]
#> [1] "R" "Julia" "Python"
[[2]]
#> [1] "C" "Kotlin," "F#"
Es gibt auch str_split_fixed, das eine Matrix zurückgibt.
Matrizen werden in einem späteren Konzept behandelt.
Um die gesamte Zeichenkette zu vergleichen, kann str_equal() verschiedene Definitionen von „Gleichheit“ verwenden.
Standardmäßig wird zwischen Groß- und Kleinschreibung unterschieden, aber das lässt sich ändern.
Locales und Unicode eröffnen noch viele weitere Möglichkeiten.
str_equal("Odin", "odin")
#> [1] FALSE
str_equal("Odin", "odin", ignore_case = TRUE)
#> [1] TRUE
Es gibt viele weitere Funktionen, die im Konzept zu regulären Ausdrücken ausführlicher besprochen werden.
Manche Sprachen (vor allem europäische) unterscheiden zwischen GROSSBUCHSTABEN und Kleinbuchstaben.
stringr bietet eine Reihe von Funktionen, um diese auf locale-gerechte Weise ineinander umzuwandeln:
str_to_lower()str_to_upper()str_to_title() : erster Buchstabe jedes Wortes großstr_to_sentence() : erster Buchstabe des Satzes großstr_to_upper("elixir") # defaults to locale = "en"
#> [1] "ELIXIR"
str_to_upper("ελληνικά") # infers Greek
#> [1] "ΕΛΛΗΝΙΚΆ"
str_to_title("the cat sat on the mat")
#> [1] "The Cat Sat On The Mat"
str_to_sentence("the cat sat on the mat")
#> [1] "The cat sat on the mat"
Zum Ersetzen von Teilzeichenketten anhand eines Musters ändert str_replace() das erste Vorkommen des Musters, str_replace_all() alle Vorkommen.
Zum Ersetzen von Teilzeichenketten anhand der Zeichenposition gibt es eine Zuweisungsform von str_sub().
s <- "abcde"
str_sub(s, 3, 4) <- "yz"
s
#> [1] "abyze"
str_dup erzeugt Duplikate und str_unique entfernt Duplikate.
Standardmäßig hat str_dup kein Trennzeichen, aber du kannst eines angeben.
str_dup("xyz", 4, sep = ", ")
#> [1] "xyz, xyz, xyz, xyz"
str_unique ist eine Erweiterung von unique().
Beide entfernen doppelte Einträge aus einem Vektor, aber die stringr-Funktion kann auch zwischen Groß- und Kleinschreibung unterscheiden und verschiedene locale-spezifische Übereinstimmungen berücksichtigen.
inp <- c("some", "strings", "Some", "Strings")
str_unique(inp)
#> [1] "some" "strings" "Some" "Strings"
str_unique(inp, ignore_case = TRUE)
#> [1] "some" "strings"
Die Bibliothek stringr baut auf der Bibliothek stringi mit grundlegenderen Funktionen auf.
Wenn du besondere Anforderungen hast, die oben noch nicht besprochen wurden, dann: