text1 <- "Mit doppelten Anführungszeichen"
text2 <- 'Wenn "Anführungszeichen" im Text vorkommen, klappen die einfachen'14 Strings
14.1 Voraussetzungen und erste Beispiele
- In diesem Kapitel werden die Grundlagen des Pakets
stringrbehandelt. - Man kann Zeichenketten, die auch Strings genannt werden, mit Hilfe von einfachen oder doppelten Anführungszeichen erstellen.
- Das einfache Beispiel zeigt schon, dass einige Sonderzeichen anders behandelt werden müssen. Man schreibt diese Sonderzeichen mit Hilfe sogenannter Escape-Sequenzen.
x <- c("\'", "\"", "\\", "\u00b5") # einfach, doppelt, Backslash, µ
x [1] "'" "\"" "\\" "µ"
writeLines(x)'
"
\
µ
Man sieht, dass die gedruckte Darstellung des Strings und der String selbst nicht automatisch das Gleiche sind!
Die komplette Liste der Escape-Sequenzen bekommt man z.B. mit der Hilfe zu Anführungszeichen:
help('"')| Escape-Sequenz | Bedeutung |
|---|---|
\n |
Neue Zeile (Zeilenvorschub) |
\r |
Wagenrücklauf |
\t |
Tabulator |
\b |
Rückschritt (Backspace) |
\a |
Signalton (Alarm / Glocke) |
\f |
Seitenvorschub (Form Feed) |
\v |
Vertikaler Tabulator |
\\ |
Rückwärtsschrägstrich (Backslash) \
|
\' |
Einfaches Anführungszeichen '
|
\" |
Doppeltes Anführungszeichen "
|
\` |
Gravis-Akzent (Backtick) `
|
\nnn |
Zeichen mit angegebenem Oktalcode (1–3 Ziffern) |
\xnn |
Zeichen mit angegebenem Hexadezimalcode (1–2 Stellen) |
\unnnn |
Unicode-Zeichen mit angegebenem Codepunkt (1–4 hexadezimale Stellen) |
\Unnnnnnnn |
Unicode-Zeichen mit angegebenem Codepunkt (1–8 hexadezimale Stellen) |
14.2 Zusammenfügen von Strings
- Das Zusammenfügen von Zeichenketten geschieht mit der Funktion
str_c(). Dabei kann ein Separator mit dem Argumentsep=angegeben werden. Außerdem werden Vektoren recycelt.
str_c("x", "y")[1] "xy"
str_c("x", "y", "z", sep = ", ") # Separator[1] "x, y, z"
str_c("x", c("y","z")) # vektorisiert -> recycling[1] "xy" "xz"
str_c(c("a", "b", "c"), collapse = ", ") # kollabiert einen Vektor[1] "a, b, c"
- Im Allgemeinen bleiben
NAs auch nach dem ZusammenfügenNAs, es sei denn, sie sollen explizit als Zeichenkette behandelt werden. Dies ist der wesentliche Unterschied zur Funktionpaste()aus Base-R.
x <- c("abc", NA)
str_c("+|", x, "|+") # NA bleibt NA[1] "+|abc|+" NA
str_c("+|", str_replace_na(x), "|+") # NA wird wie Zeichenkette behandelt[1] "+|abc|+" "+|NA|+"
14.3 Teile von Zeichenketten
- Mit der Funktion
str_sub()kann auf Teile der Zeichenkette zugegriffen werden. Dabei geben die Argumentestart=undend=die betroffenen Positionen an.
x <- c("Kanne", "Rand", "Laden")
str_sub(x, 1, 3)[1] "Kan" "Ran" "Lad"
str_sub(x, -3, -1) # Negative Zahlen zählen von hinten[1] "nne" "and" "den"
- Es spielt keine Rolle, wenn die Zeichenkette zu kurz ist:
y <- c("A")
str_sub(y, 1, 5)[1] "A"
- Es ist auch möglich, Zuweisungen zu machen:
str_sub(x,1,1) <- "w"
x[1] "wanne" "wand" "waden"
str_sub(x,1,1) <- str_to_upper(str_sub(x,1,1))
x[1] "Wanne" "Wand" "Waden"
14.3.1 Locales
- Wir hatten schon die Funktion
str_to_upper()gesehen. Analog gibt es auch:
[1] "DAS IST EIN STRING" "IN" "3 TEILEN"
str_to_lower(x)[1] "das ist ein string" "in" "3 teilen"
str_to_title(x)[1] "Das Ist Ein String" "In" "3 Teilen"
str_to_sentence(x)[1] "Das ist ein string" "In" "3 teilen"
- Gegebenenfalls muss das Argument
locale=gesetzt werden.
# Türkisch hat zwei i's: mit und ohne Punkt. Beim Kapitalisieren muss das
# ggf. durch Setzen eines locale-Arguments berücksichtigt werden:
str_to_upper(c("i", "ı"))[1] "I" "I"
str_to_upper(c("i", "ı"), locale = "tr")[1] "İ" "I"
- Auch die Funktionen
str_sort()undstr_order()haben das Argumentlocale=, da verschiedene Sprachen verschiedene Ordnungen in den Alphabeten haben.
14.4 Reguläre Ausdrücke
Einfaches Identifizieren von Mustern
- Ein wichtiges und effizientes Werkzeug, um Muster in Zeichenketten zu identifizieren, sind reguläre Ausdrücke.
- Um das identifizierte Muster zu visualisieren, bietet sich die Funktion
str_view()an.
punkt_und_backslash <- c("\\.", "\\\\")
writeLines(punkt_und_backslash)\.
\\
x <- c("Apfel", "Banane", "Mango", "Traube")
str_view(x, "an")[2] │ B<an><an>e
[3] │ M<an>go
x <- c("Apfel", "Banane", "Mango", "Traube")
str_view(x, ".a.")[2] │ <Ban>ane
[3] │ <Man>go
[4] │ T<rau>be
Während die Syntax im oberen Beispiel klar sein sollte, ist im unteren Beispiel die Bedeutung der Punkte nicht sofort klar: Es wird nicht nach Punkten in einem Ausdruck gesucht, sondern die Punkte sind Platzhalter. Eine Frage, die sich nun stellt, ist: Wie identifiziert man „.“? Dazu benötigt man eine Escape-Sequenz.
14.4.1 Anker und Zeichenklassen
Reguläre Ausdrücke betreffen prinzipiell jeden Teil eines Strings. Manchmal möchte man gezielt am Anfang oder am Ende oder nach Klassen von Zeichen suchen.
-
Anfang oder Ende:
-
^der Anfang eines Strings, -
$das Ende eines Strings.
-
-
Klassen von Zeichen:
-
\deine beliebige Ziffer, -
\sein beliebiges Leerzeichen (z.B. Leertaste, Tab, neue Zeile), -
[abc]die Buchstaben a, b oder c, -
[^abc]alles außer den Buchstaben a, b oder c.
-
Wichtig: Um einen regulären Ausdruck mit \d oder \s zu generieren, muss \ escaped werden, und man schreibt:
writeLines(c("\\d", "\\s"))\d
\s
x <- c("abcd", "dcba", "a")
# Anfang der Zeichenkette
str_view(x, "^a") [1] │ <a>bcd
[3] │ <a>
# Ende der Zeichenkette
str_view(x, "a$") [2] │ dcb<a>
[3] │ <a>
Beispiele: Zeichenklassen
x <- c("1 2 3 abc", "ABC 123", "abc ABC")
str_view(x, "[bcd]") [1] │ 1 2 3 a<b><c>
[3] │ a<b><c> ABC
str_view(x, "\\d") [1] │ <1> <2> <3> abc
[2] │ ABC <1><2><3>
Mit Hilfe von | kann zwischen alternativen Möglichkeiten gesucht werden (Vorsicht, schwache Bindungsstärke):
str_view(c("Stefan", "Stephan"), "Ste(f|ph)an") [1] │ <Stefan>
[2] │ <Stephan>
str_view(c("Stefan", "Stephan"), "Stef|phan") [1] │ <Stef>an
[2] │ Ste<phan>
- Eine Zeichenklasse, die genau ein Zeichen enthält, ist eine Alternative zu Backslash-Escape-Sequenzen.
- Dies funktioniert mit fast allen Sonderzeichen:
$,.,|,?,*,+,(,),],{,}. - Allerdings nicht mit
[,\,^,-, da diesen eine besondere Bedeutung innerhalb der regulären Ausdrücke zukommt. Diese müssen mittels Backslash escaped werden.
y <- c("abc", "a.c", "a*c", "a c")
str_view(y, "a[.]c") [2] │ <a.c>
str_view(y, ".[*]c") [3] │ <a*c>
str_view(y, "a[ ]") [4] │ <a >c
14.5 Wiederholungen
-
Der nächste Schritt ist, zu kontrollieren, wie oft ein Muster auftaucht:
-
?: 0 oder 1 -
+: 1 oder mehr -
*: 0 oder mehr
-
Die Bindungsstärke der Operatoren ist sehr hoch, so dass meist runde Klammern gesetzt werden müssen. Der Ausdruck
wie?derpasst sowohl aufwiderals auch aufwieder.-
Es ist auch möglich, die Zahl der Wiederholungen genau zu spezifizieren:
-
{n}: genau n -
{n,}: n oder mehr -
{,m}: höchstens m -
{n,m}: zwischen n und m
-
Dabei wird der längste mögliche String herausgesucht. Setzt man ein ? hinter den Ausdruck, so wird der kürzeste mögliche String herausgesucht.
x <- "1888 ist MDCCCLXXXVIII"
str_view(x, "CC?") [1] │ 1888 ist MD<CC><C>LXXXVIII
str_view(x, "C[LX]+")[1] │ 1888 ist MDCC<CLXXX>VIII
str_view(x, "C{2,3}")[1] │ 1888 ist MD<CCC>LXXXVIII
str_view(x, "C{2,3}?")[1] │ 1888 ist MD<CC>CLXXXVIII
14.5.1 Gruppierungen und Rückverweise
- Mit den runden Klammern
()kann man nicht nur komplexe Ausdrücke eindeutig formulieren, sie erlauben es auch nummerierte (1, 2, usw.) Gruppen (numbered capturing group) zu bilden. - Dabei wird der reguläre Ausdruck innerhalb der Klammern gespeichert, und es ist möglich, mittels
\1,\2etc. darauf zu verweisen.
str_view(fruit, "(..)\\1", match = TRUE) [4] │ b<anan>a
[20] │ <coco>nut
[22] │ <cucu>mber
[41] │ <juju>be
[56] │ <papa>ya
[73] │ s<alal> berry
str_view(fruit, "(o)\\1", match = TRUE) [9] │ bl<oo>d orange
[33] │ g<oo>seberry
str_view(fruit, "(.)(.)\\2\\1", match = TRUE) [5] │ bell p<eppe>r
[17] │ chili p<eppe>r
str_view(fruit, "(.)(.)(.)\\2\\1", match = TRUE)[4] │ b<anana>
Anwendungen
Nachdem reguläre Ausdrücke nun bekannt sind, wird es Zeit, diese sinnvoll einzusetzen. Folgende Themen sollen dazu behandelt werden:
- Zeichenketten finden, die ein bestimmtes Muster haben,
- Positionen eines Musters finden,
- Muster extrahieren,
- Muster ersetzen,
- Strings aufgrund eines Musters teilen.
Reguläre Ausdrücke sind sehr mächtig, und es ist verlockend, Probleme mit einem einzigen regulären Ausdruck zu lösen. Aber:
„Some people, when confronted with a problem, think ‘I know, I’ll use regular expressions.’ Now they have two problems.“
(Jamie Zawinski)
Ein Beispiel ist dieser reguläre Ausdruck, der überprüft, ob eine E-Mail-Adresse zulässig ist:
14.6 Übereinstimmungen erkennen
- Ob ein Muster in einer Zeichenkette enthalten ist oder nicht, kann mit der Funktion
str_detect()herausgefunden werden.
x <- c("Apfel", "Birne", "Dattel")
str_detect(x, "l")[1] TRUE FALSE TRUE
- Da die logischen Werte
TRUEundFALSEin einem numerischen Kontext zu1und0werden, ist es möglich, auf längeren Vektoren statistische Kenngrößen zu berechnen.
# Einlesen 370105 engl. Wörter
words <- read_lines("https://tinyurl.com/3t569b4v")
# Wörter der Länge 5:
wordle <- words[str_length(words) == 5]
# eine Übersicht:
summary(wordle) Length N.unique N.blank Min.nchar Max.nchar
15921 15921 0 5 5
# Wie viele starten mit einem t:
sum(str_detect(wordle, "^t"))[1] 981
# Wie viele enthalten Doppelkonsonanten:
sum(str_detect(wordle, "([^aeiou])\\1"))[1] 1358
# Wie viele haben keine Vokale:
sum(!str_detect(wordle, "[aeiou]")) [1] 54
sum(str_detect(wordle, "^[^aeiou]+$")) [1] 54
# Anteil der Wörter, die auf einen Vokal enden:
mean(str_detect(wordle, "[aeiou]$"))[1] 0.2743546
- Um Wörter mit einer bestimmten Sequenz oder einem bestimmten Muster zu filtern, kann die Funktion
str_subset()verwendet werden:
# kompliziert:
wordle[str_detect(wordle, "axy$")][1] "ataxy" "braxy" "coaxy" "flaxy"
# oder die Kurzform:
str_subset(wordle, "axy$")[1] "ataxy" "braxy" "coaxy" "flaxy"
- Sind die Daten in einem Dataframe, so können die Funktionen analog in der Funktion
filter()verwendet werden.
# A tibble: 2 × 2
woerter i
<chr> <int>
1 rebuy 11404
2 upbuy 14914
- Eine Variation von
str_detect()iststr_count(), dabei wird die Anzahl der Übereinstimmungen in einer Zeichenkette gezählt.
x <- c("Augsburg", "Atlanta")
str_count(x, "a")[1] 0 2
# Durchschnittliche Vokalzahl
mean(str_count(wordle, "[aeiou]"))[1] 1.874443
- Es ist zu bemerken, dass Treffer niemals überlappen, wie das folgende Beispiel zeigt:
# liefert 2 und nicht 3 Treffer:
str_count("abababa", "aba")[1] 2
14.7 Übereinstimmungen extrahieren
- Um den Text einer Übereinstimmung herauszuziehen, benutzt man die Funktion
str_extract(). - Um die Funktionsweise zu illustrieren, nehmen wir die folgenden Daten:
length(sentences)[1] 720
head(sentences) [1] "The birch canoe slid on the smooth planks."
[2] "Glue the sheet to the dark blue background."
[3] "It's easy to tell the depth of a well."
[4] "These days a chicken leg is a rare dish."
[5] "Rice is often served in round bowls."
[6] "The juice of lemons makes fine punch."
- Als Beispiel sollen nun die in den Sätzen enthaltenen Farben extrahiert werden. Dazu bildet man einen regulären Ausdruck, der die Farben enthält:
colours <- c("red", "orange", "yellow", "green", "blue", "purple")
colour_match <- str_c(colours, collapse = "|")
colour_match[1] "red|orange|yellow|green|blue|purple"
- Nun filtert man die Sätze heraus, die mindestens eine der Farben enthalten:
has_colours <- str_subset(sentences, colour_match)
matches <- str_extract(has_colours, colour_match)
head(matches)[1] "blue" "blue" "red" "red" "red" "blue"
- Die Funktion
str_extract()extrahiert immer nur den ersten Treffer.
Das sieht man, wenn man die Sätze auswählt, die mehr als eine Übereinstimmung haben:
more <- sentences[str_count(sentences, colour_match) > 1]
str_view(more, colour_match)- Mit der Funktion
str_extract_all()erhält man alle Übereinstimmungen. Das Ergebnis ist eine Liste. Listen sind eine praktische Datenstruktur, auf die wir später nochmal zurückkommen. Wählt man zusätzlich das Argumentsimplify = TRUE, so erhält man eine Matrix, eine weitere Datenstruktur, die nicht nur für die lineare Algebra praktisch ist ;-).
x <- c("a", "b-c", "a.b.c")
str_extract_all(x, "[a-z]")[[1]]
[1] "a"
[[2]]
[1] "b" "c"
[[3]]
[1] "a" "b" "c"
str_extract_all(x, "[a-z]", simplify = TRUE) [,1] [,2] [,3]
[1,] "a" "" ""
[2,] "b" "c" ""
[3,] "a" "b" "c"
14.8 Gruppierte Übereinstimmungen
Für die runden Klammern
()gibt es neben den bisher besprochenen noch eine weitere Anwendung, nämlich um Teile eines komplexen Ausdrucks zu extrahieren.Im folgenden Beispiel wird jedes Wort gesucht, das nach den Artikeln a, an oder the kommt.
noun <- "(a|an|the) ([^ ]+)"
has_noun <- sentences |>
str_subset(noun) |>
head(6)
has_noun |> str_extract(noun)[1] "the smooth" "the sheet" "the depth" "a chicken"
[5] "the parked" "the sun"
Bemerkung: Es ist ein wenig kompliziert in einem regulären Ausdruck ein Wort zu definieren. Der obige Ausdruck ist eine einfache Näherung und für viele Anwendungen geeignet (hier allerdings nur sehr bedingt).
- Mit der Funktion
str_match()erhält man jede einzelne Komponente in Form einer Matrix:
has_noun |> str_match(noun) [,1] [,2] [,3]
[1,] "the smooth" "the" "smooth"
[2,] "the sheet" "the" "sheet"
[3,] "the depth" "the" "depth"
[4,] "a chicken" "a" "chicken"
[5,] "the parked" "the" "parked"
[6,] "the sun" "the" "sun"
- Hat man die Daten in einem Tibble, so ist die Funktion
tidyr::extract()einfacher zu benutzen. Diese macht das Gleiche wiestr_match(), allerdings muss man für die Übereinstimmungen Namen angeben, da die Ergebnisse als eigene Merkmale gespeichert werden.
# A tibble: 720 × 3
sentence article noun
<chr> <chr> <chr>
1 The birch canoe slid on the smooth planks. the smooth
2 Glue the sheet to the dark blue background. the sheet
3 It's easy to tell the depth of a well. the depth
4 These days a chicken leg is a rare dish. a chicken
5 Rice is often served in round bowls. <NA> <NA>
6 The juice of lemons makes fine punch. <NA> <NA>
7 The box was thrown beside the parked truck. the parked
8 The hogs were fed chopped corn and garbage. <NA> <NA>
9 Four hours of steady work faced us. <NA> <NA>
10 A large size in stockings is hard to sell. <NA> <NA>
# ℹ 710 more rows
- Analog zu
str_match()benötigt manstr_match_all(), wenn alle Übereinstimmungen pro String erkannt werden sollen.
14.8.1 Übereinstimmungen ersetzen
- Die Funktionen
str_replace()undstr_replace_all()erlauben es, Übereinstimmungen durch andere Zeichenketten zu ersetzen. - Mit der Funktion
str_replace_all()können auch mehrere (verschiedene) Ersetzungen gleichzeitig durchgeführt werden, wenn die ausgetauschte Zeichenkette ein (benannter) Vektor ist. - Man kann auch Rückverweise nutzen, um Übereinstimmungen zu ersetzen oder zu tauschen (im Beispiel wird das zweite und das dritte Wort getauscht).
x <- c("Apfel", "Birne", "Clementine")
str_replace(x, "[aeiou]", "-")[1] "Apf-l" "B-rne" "Cl-mentine"
str_replace_all(x, "[aeiou]", "-")[1] "Apf-l" "B-rn-" "Cl-m-nt-n-"
[1] "ein Auto" "zwei Kinder" "drei CDs"
sentences |>
str_replace("([^ ]+) ([^ ]+) ([^ ]+)", "\\1 \\3 \\2") |>
head(4)[1] "The canoe birch slid on the smooth planks."
[2] "Glue sheet the to the dark blue background."
[3] "It's to easy tell the depth of a well."
[4] "These a days chicken leg is a rare dish."
14.9 Splitting
- Die Funktion
str_split()zerlegt eine Zeichenkette. Die Argumente sind:-
string=: die zu zerlegende Zeichenkette, -
pattern=: das Muster, an dem die Zeichenkette getrennt wird. Dieses wird komplett entnommen, ist also in der getrennten Ausgabeliste nicht mehr enthalten, -
n=Inf: Anzahl der zurückgegebenen Stücke und -
simplify=FALSE: Rückgabe ist eine Liste, fallsTRUE, wird eine Zeichenketten-Matrix zurückgegeben.
-
sentences |> head(3) |> str_split(" ")[[1]]
[1] "The" "birch" "canoe" "slid" "on" "the"
[7] "smooth" "planks."
[[2]]
[1] "Glue" "the" "sheet" "to"
[5] "the" "dark" "blue" "background."
[[3]]
[1] "It's" "easy" "to" "tell" "the" "depth" "of" "a"
[9] "well."
str_split(c("a|b|c|d"), "\\|")[[1]]
[1] "a" "b" "c" "d"
14.10 Splitting und Muster finden
# als Matrix
sentences |> head(3) |> str_split(" ", simplify = TRUE) [,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] "The" "birch" "canoe" "slid" "on" "the" "smooth"
[2,] "Glue" "the" "sheet" "to" "the" "dark" "blue"
[3,] "It's" "easy" "to" "tell" "the" "depth" "of"
[,8] [,9]
[1,] "planks." ""
[2,] "background." ""
[3,] "a" "well."
# höchstens je 2 Teile
sentences |> head(3) |> str_split(" ", n = 2, simplify = TRUE) [,1] [,2]
[1,] "The" "birch canoe slid on the smooth planks."
[2,] "Glue" "the sheet to the dark blue background."
[3,] "It's" "easy to tell the depth of a well."
- Mit den Funktionen
str_locate()undstr_locate_all()erhält man den Start- und Endpunkt eines Musters. Dies ist genau dann sinnvoll, wenn keine der anderen Funktionen exakt das macht, was man braucht. (\implies?str_locate) - Mit
str_locate()können passende Muster gefunden und mitstr_sub()anschließend extrahiert oder geändert werden.