Einführung in R: Zeichen

In R sind Zeichen kein fundamentaler Datentyp; sie werden nicht von Zeichenketten unterschieden. Um aber mit den Besonderheiten der Verarbeitung von Zeichen vertraut zu werden, werden vorerst nur Zeichen behandelt. Diese Besonderheiten betreffen insbesondere die Maskierung von bestimmten Zeichen und die Typumwandlungen. Einige harmlose Beispiele für Zeichenketten (die ohne Wissen über Vektoren verständlich sind) werden hier schon angeführt.

Einordnung des Artikels

Einführung: Zeichen und Zeichenketten

In den meisten Programmiersprachen wird streng zwischen Zeichen und Zeichenketten unterschieden; oft werden Zeichen mit Hochkommas und Zeichenketten mit Anführungsstrichen gekennzeichnet. In C++ sieht dies etwa folgendermaßen aus:

// Achtung C++
char c  = 'x';
string s = "Hello World";

Intern werden die Zeichenketten dann wie Felder von einzelnen Zeichen behandelt — als Programmierer verwendet man aber nicht diese Felder von Zeichen sondern den Datentyp string.

In der Übersicht über Datentypen in R wurde gezeigt, dass es in R keine Unterscheidung zwischen Zahlen und Vektoren von Zahlen gibt. Entsprechend gibt es hier auch keine Unterscheidung zwischen Zeichen und Zeichenketten — es gibt nur den Modus character, der sowohl für einzelne Zeichen als auch für Zeichenketten verwendet wird. Hier in der ersten Einführung zu Zeichen werden Zeichen aber wie ein fundamentaler Datentyp behandelt — Zeichenketten werden dann erst später besprochen, nachdem die Eigenschaften von Vektoren erklärt wurden.

Definition von Zeichen und Zeichenketten

Zur Definition eines Zeichens oder einer Zeichenkette können sowohl

verwendet werden.

Sie markieren Beginn und Ende eines Zeichens beziehungsweise einer Zeichenkette. Bei einer Ausgabe werden stets Anführungsstriche gezeigt (wie man dies steuern kann, wird weiter unten erklärt).

Das folgende Skript zeigt, dass beide Versionen syntaktisch korrekt sind:

c1 <- 'x'
s1 <- "Hello World"

c1
# [1] "x"
s1
# [1] "Hello World"

c2 <- "x"
s2 <- 'Hello World'

c2
# [1] "x"
s2
# [1] "Hello World"

Um die Quelltexte leichter lesbar zu gestalten, wird eine der beiden Strategien empfohlen:

  1. Entweder für Zeichen immer Hochkommas und für Zeichenketten immer Anführungsstriche verwenden.
  2. Oder immer Anführungsstriche verwenden.

Die erste Strategie hat den Vorteil, dass die Quelltexte wie in vielen anderen Programmiersprachen aussehen. Die zweite Strategie spart einige Verwirrung mit den zu maskierenden Zeichen (mehr zum Thema Maskierung von Zeichen folgt sofort).

Ausgabe von Zeichen und Zeichenketten

Maskierung von Anführungsstrichen und des backslash-Symbols

Es ist klar, dass das Zeichen, das Beginn und Ende eines Zeichens oder einer Zeichenkette markiert, nicht als Zeichen verwendet werden kann.

Das folgende Skript versucht einen Anführungsstrich " (double quotation mark) als Zeichen zu definieren — und gleichzeitig Anführungsstriche als begrenzende Zeichen einzusetzen:

# Versuch ein Zeichen mit Inhalt " zu definieren
quot <- """
# Syntax-Fehler

Die rechte Seite der Zuweisung ist natürlich syntaktisch inkorrekt. Um dennoch einen Anführungsstrich " innerhalb eines Zeichens (oder einer Zeichenkette) darzustellen, muss es mit dem backslash-Symbol \ maskiert werden:

quot <- "\""

quot
# [1] "\""

Analoges gilt dann natürlich für Hochkommas, wenn diese als begrenzende Zeichen eingesetzt werden.

Es gelten also folgende Regeln:

  1. Werden Anführungsstriche zur Definition von Zeichen (oder Zeichenketten) eingesetzt, müssen Anführungsstriche maskiert werden.
  2. Werden Hochkommas zur Definition von Zeichen (oder Zeichenketten) eingesetzt, müssen Hochkommas maskiert werden.

Und da jetzt das backslash-Symbol \ eine spezielle Bedeutung zur Definition von Zeichen hat, muss auch dieses maskiert werden — nämlich wiederum mit dem backslash-Symbol \ :

bsl <- "\\"

bsl
# [1] "\\"

Die beiden letzten Skripte werfen aber sofort eine weitere Frage auf:

Wie werden maskierte Zeichen ausgegeben?

Es sind ja zwei Möglichkeiten denkbar (hier für das Beispiel, dass das backslash-Symbol definiert werden soll):

  1. So wie es definiert wurde, also mit dem Maskierungs-Zeichen: hier also \\
  2. So wie das Zeichen dargestellt werden soll, also ohne das Maskierungs-Zeichen: \ .

Man sieht an den Ausgaben der Skripte, dass die erste Variante realisiert wird. Möchte man selber bestimmen, wie eine Ausgabe zu erfolgen hat kann man dies mit den Funktionen:

  1. print.default(): Die Ausgabe erfolgt mit den Maskierungs-Zeichen (also zum Beispiel \\ für das backslash-Symbol) und man kann festlegen, ob die begrenzenden Anführungsstriche gezeigt werden oder nicht.
  2. writeLines(): Die Ausgabe erfolgt ohne Maskierungs-Zeichen \ und ohne die begrenzenden Anführungsstriche.

Diese beiden Funktionen werden in den folgenden Abschnitten vorgestellt.

Die Funktion print.default()

In den Skripten oben wurde die Ausgabe stets durch die Angabe der Variable ausgelöst. Man muss sich dazu aber vergegenwärtigen, dass sich dahinter der Aufruf der print()-Funktion verbirgt, genauer die Funktion print.default(). Diese ist so implementiert, dass Zeichen (und Zeichenketten) so ausgegeben werden, wie sie definiert wurden und dass als begrenzende Zeichen die Anführungsstriche verwendet werden. Letzteres ist die default-Einstellung, die man bei Bedarf anpassen kann.

Früher wurde schon gesagt, dass Funktionen wie print() generisch implementiert sind, das heißt sie werden für jeden Datentyp geeignet implementiert. Ist aber zu einem Datentyp keine eigene Implementierung vorhanden, wird die entsprechende default-Funktion aufgerufen — hier also print.default(). Das heißt, ruft man die print()-Funktion für ein Zeichen (oder eine Zeichenkette) auf, wird der Funktionsaufruf an print.default() weitergereicht, da es für den Modus character keine eigene Implementierung gibt. Man kann dies auch anders lesen: es reicht bei Zeichen und Zeichenketten die Funktion print() aufzurufen, wenn man eigentlich print.default() aufrufen möchte.

Die Funktion print.default() besitzt neben weiteren Argumenten, die hier nicht besprochen werden, die beiden Argumente:

Das folgende Skript zeigt für das Zeichen c <- '!' :

c <- '!'

c
# [1] "!"

print(x = c)
# [1] "!"

print.default(x = c)
# [1] "!"

print(x = c, quote = FALSE)
# [1] !

print.default(x = c, quote = FALSE)
# [1] !

Zur Erklärung:

Zeile 1: Die Variable c wird durch das Ausrufezeichen definiert (hier erfolgt die Definition mit Hochkommas).

Zeile 3: Die abgekürzte Ausgabe von c zeigt das Ausrufezeichen mit den begrenzenden Anführungsstrichen.

Zeile 6 und 9: Die Funktionen print() und print.default() führen zum identischen Ergebnis wie in Zeile 3.

Zeile 12 und 15: Wird dagegen zusätzlich das Argument quote = FALSE gesetzt, fehlen die begrenzenden Anführungsstriche.

Das selbe Skript wird nochmals mit bsl <- '\\' ausgeführt, also mit dem backslash-Symbol:

bsl <- '\\'

bsl
# [1] "\\"

print(x = bsl)
# [1] "\\"

print.default(x = bsl)
# [1] "\\"

print(x = bsl, quote = FALSE)
# [1] \\

print.default(x = bsl, quote = FALSE)
# [1] \\

Die Funktion writeLines()

Die Funktion writeLines() ist eigentlich dazu da, einen Text in eine Datei zu schreiben. Die default-Einstellung ist aber, dass der Text auf der Konsole ausgegeben wird. Weitere Eigenschaften von writeLines() sind:

Das folgende Beispiel zeigt die Ausgabe von bsl <- '\' , also des backslash-Symbols, mit writeLines():

bsl <- '\\'

writeLines(text = bsl)
# \

Man sieht, dass in der Ausgabe jetzt sogar die Zählung der Vektor-Komponente [1] fehlt.

Zeichen, die mit dem backslash-Symbol maskiert werden müssen

Neben dem Anführungsstrich " , dem Hochkomma ' und dem backslash-Symbol \ gibt es noch weitere Zeichen, die mit dem backslash-Symbol maskiert werden müssen. Man kann sie in drei Gruppen einteilen:

  1. Steuerzeichen
  2. Sonderzeichen
  3. Zeichen aus dem ASCII-Code oder Unicode, wobei deren oktale oder hexadezimale Codierung angegeben werden muss.

Die folgende Tabelle zeigt die ersten beiden Gruppe dieser Symbole, die man unter ?Quotes in der R-Dokumentation finden kann:

Zeichen Beschreibung
\n newline
\r carriage return
\t tab
\b backspace
\a alert (bell)
\f form feed
\v vertical tab
\\ backslash
\' ASCII apostrophe
\" ASCII quotation mark
\` ASCII grave accent (backtick)
?Quotes Dokumentation: Weitere Informationen im package base unter Quotes

Es folgt ein Beispiel, um die wichtigsten Zeichen newline und tab zu erklären; man beachte, dass zwischen newline '\n' und Lichtenberg

quote <- "\"Wahrhaftigkeit ist die größte List.\" \n Lichtenberg"
quote.tab <- "\"Wahrhaftigkeit ist die größte List.\" \n\tLichtenberg"
quote.tabtab <- "\"Wahrhaftigkeit ist die größte List.\" \n\t\tLichtenberg"

print(quote)
# [1] "\"Wahrhaftigkeit ist die größte List.\" \n Lichtenberg"

print(quote.tab)
# [1] "\"Wahrhaftigkeit ist die größte List.\" \n\tLichtenberg"

print(quote.tabtab)
# [1] "\"Wahrhaftigkeit ist die größte List.\" \n\t\tLichtenberg"

writeLines(quote)
# "Wahrhaftigkeit ist die größte List." 
#  Lichtenberg

writeLines(quote.tab)
# "Wahrhaftigkeit ist die größte List." 
#   Lichtenberg

writeLines(quote.tabtab)
# "Wahrhaftigkeit ist die größte List." 
#       Lichtenberg

Wenn Sie das Skript selbst ausführen, kann die Ausgabe anders aussehen — je nachdem wie in Ihrer Entwicklungsumgebung die tab width für die Konsolen-Ausgabe eingestellt ist.

Auf den ASCII-Code und Unicode wird hier nicht eingegangen.

Weitere Funktionen

Weiter gibt es in R die Funktionen encodeString() und cat(), die in diesem Zusammenhang erwähnt werden sollten.

Die Funktion encodeString() dafür sorgt, die Maskierungs-Zeichen zu erzeugen:

y <- 'a"b"c\nd'
encodeString(y)
# [1] "a\"b\"c\\nd"

Die Funktion soll hier nicht näher erklärt werden.

Die Funktion cat() dagegen interpretiert die Maskierungs-Zeichen; sie soll hier aber noch nicht erklärt werden, da zu ihrem Verständnis einiges Wissen über Vektoren nötig ist.

Diagnose-Funktionen

Bei den Diagnose-Funktionen für Zeichen kann unterschieden werden zwischen:

  1. Funktionen zur Ausgabe (wie print.default() und writeLines()),
  2. Funktionen, die den Modus oder Speicher-Modus abfragen,
  3. is-dot-Funktionen,
  4. Abfrage der Anzahl der Zeichen einer Zeichenkette mt nchar(),
  5. Abfrage der Struktur mit str().

Funktionen zur Ausgabe wurden oben bereits ausführlich beschrieben; dies wird hier nicht wiederholt.

In der Übersicht über Datentypen in R wurde bereits geschildert, dass:

Die einzige is-dot-Funktion, die es für "character" gibt, ist is.character(). Mit ihr kann von einem gegebenen Objekt x untersucht werden, ob der Modus von x gleich "character" ist; der Rückgabewert ist ein logischer Wert.

Bei der Abfrage der Struktur eines Zeichens werden der Modus und der Inhalt des Zeichens ausgegeben.

Das folgende Skript zeigt den Einsatz dieser Diagnose-Funktionen für das Zeichen '5' :

c <- '5'

mode(c)
# [1] "character"

storage.mode(c)
# [1] "character"

is.character(c)
# [1] TRUE

is.numeric(c)
# [1] FALSE

nchar(c)
# [1] 1

str(c)
# chr "5"

Es soll noch gezeigt werden, wie nchar() auf Maskierungs-Zeichen reagiert:

nchar('!')
# [1] 1

nchar('\\')
# [1] 1

nchar("\"a\"")
# [1] 3

Man sieht an den Ausgaben, dass die Maskierungs-Zeichen nicht zur Anzahl der Zeichen in einer Zeichenkette gerechnet werden. Der Rückgabewert von nchar() ist eine Zahl im Speicher-Modus "integer".

Die Funktion nchar() besitzt noch weitere Argumente, die hier nicht besprochen werden. Der Eingabewert x kann auch ein Vektor vom Modus "character" sein; wie nchar() dann arbeitet, wird später bei den Zeichenketten besprochen.

Typumwandlungen (coercion)

Die folgende Tabelle zeigt, welche Typumwandlungen zwischen den Modi logical, numeric und character möglich sind; die Umwandlungen zwischen den Speicher-Modi integer und double wurden in der Übersicht über Datentypen in R bereits ausführlich erklärt und sind in die Tabelle nicht aufgenommen. Die ausführliche Erläuterung der Tabelle folgt unten.

Modus 1. 2. 3. 4. 5.
"logical" TRUE FALSE TRUE TRUE
Typumwandlung ↑ ↓ ↑ ↓
"numeric" 1 0 5 1.01 NA
Typumwandlung ↑ ↓ ↑ ↓ ↑ ↓ ↑ ↓
"character" "1" "0" "5" "1.01" "f"

Die Typumwandlungen in der Tabelle erfolgen mit den as-dot-Funktionen, genauer:

1. Spalte:

Die Umwandlung von oben nach unten ist immer möglich. Geht man vom logischen Wert TRUE aus, wird daraus die Zahl 1 und daraus wiederum das Zeichen "1" .

Geht man umgekehrt vom Zeichen "1" aus, so kann dies in die Zahl 1 und diese in den logischen Wert TRUE verwandelt werden.

Man kann diese Ergebnisse auch so formulieren:

Der logische Wert TRUE, die Zahl 1 und das Zeichen "1" können eindeutig in beliebiger Richtung ineinander umgewandelt werden

2. Spalte:

Startet man mit dem logischen Wert FALSE, so wird daraus die Zahl 0 und daraus wiederum das Zeichen "0" .

Umgekehrt wird aus dem Zeichen "0" die Zahl 0 und daraus FALSE.

Oder in der alternativen Formulierung:

Der logische Wert FALSE, die Zahl 0 und das Zeichen "0" können eindeutig in beliebiger Richtung ineinander umgewandelt werden

3. Spalte:

Stellt ein Zeichen eine Zahl dar (hier das Zeichen "5" die Zahl 5), so kann sie den Modus numeric annehmen; verwandelt man sie in einen logischen Wert, so ist dieser natürlich gleich TRUE (nur die 0 wird bekanntlich zu FALSE).

Die Umkehrung der Umwandlung von 5 in TRUE ist hier nicht möglich: TRUE kann nur in 1 verwandelt werden, aber nicht in eine andere Zahl.

Die Umwandlung einer Zahl in ein Zeichen ist ebenso immer möglich; bei mehrstelligen Zahlen ergeben sich Zeichenketten (dies gilt dann auch für echte Gleitkommazahlen, wie man in der 4. Spalte sieht).

Diese Ergebnisse könnte man auch so formulieren:

4. Spalte:

Gegenüber der 4. Spalte ist hier nur neu, dass auch eine Gleitkommazahl in den logischen Wert TRUE verwandelt wird.

Die Ergebnisse der 3. und 4. Spalte könnte man auch so formulieren:

5. Spalte:

Geht man von einem Zeichen aus, das keiner Zahl entspricht, hier "f" , kann kein numerischer Wert erzeugt werden. Stattdessen erhält man NA. Die Angabe NA steht für not available, dessen genaue Bedeutung später erklärt wird.

Die folgende Tabelle zeigt die relevanten Fälle, wenn eine Umwandlung zwischen einem logischen Wert und einem Zeichen (oder einer Zeichenkette) vorgenommen werden soll. Verwendet werden hier die as-dot-Funktionen as.character() und as.logical() .

Modus 1. 2. 3. 4. 5. 6.
"logical" TRUE TRUE FALSE FALSE NA NA
Typumwandlung ↑ ↓ ↑ ↓
"character" "TRUE" "T" "FALSE" "F" "1" "0"

1. Spalte:

Der logische Wert TRUE und die Zeichenkette "TRUE" können ineinander umgewandelt werden.

2. Spalte:

Da T anstelle des logischen Wertes TRUE eingesetzt werden kann, lässt sich auch das Zeichen "T" in den logischen Wert TRUE verwandeln; verwandelt man allerdings TRUE in eine Zeichenkette, erhält man — siehe 1. Spalte — "TRUE".

3. Spalte:

Wie 1. Spalte, aber für FALSE und "FALSE".

4. Spalte:

Wie 2. Spalte: F kann als Abkürzung für den logischen Wert FALSE verwendet werden.

5. Spalte:

Nachdem oben gesagt wurde, dass das Zeichen "1" in die Zahl 1 und diese in den logischen Wert TRUE verwandelt werden kann, ist die 5. Spalte vielleicht überraschend. Das Zeichen "1" kann nicht direkt in einen logischen Wert verwandelt werden — es ergibt sich NA (not available).

6. Spalte:

Entsprechend kann "0" nicht in FALSE verwandelt werden.

Aufgabe.

Schreiben Sie ein Skript, mit dem Sie sämtliche Umwandlungen aus den Tabellen überprüfen können.

Zusammenfassung

Funktionen zur Ausgabe von Zeichen

Die folgende Tabelle listet die oben besprochenen Funktionen auf, mit denen Zeichen ausgegeben werden können; in der rechten Spalte wird kurz ihre Aufgabe beschrieben:

Funktion Beschreibung
print(x, quote = TRUE) Falls das Objekt x ein Zeichen (oder eine Zeichenkette) ist, wird der Funktionsaufruf an print.default(x, quote) weitergereicht. Der default-Wert von quote ist TRUE.
print.default(x, quote = TRUE) Ausgabe des Objektes x mit maskierenden Zeichen; der logische Wert quote gibt an, ob die begrenzenden Anführungsstriche gezeigt werden sollen. Der default-Wert von quote ist TRUE.
writeLines(text) Ausgabe des Zeichens (oder der Zeichenkette) text. Maskierungs-Zeichen werden nicht angezeigt, begrenzende Anführungsstriche werden unterdrückt.

Die folgende Tabelle beschreibt die Rückgabewerte der Funktionen und den Datentyp des Rückgabewertes:

Funktion Rückgabewert Datentyp
print(x, quote = TRUE) Das Objekt x Datentyp von x (Modus und Speicher-Modus sind unverändert)
print.default(x, quote = TRUE) Das Objekt x Datentyp von x (Modus und Speicher-Modus sind unverändert)
writeLines(text) kein Rückgabewert, dient zur Ausgabe von text NULL (kein Rückgabewert)

Diagnose-Funktionen

Die folgende Tabelle listet die oben besprochenen Diagnose-Funktionen auf; in der rechten Spalte wird kurz ihre Aufgabe beschrieben:

Funktion Beschreibung
mode(x) Abfrage des Modus von x
mode(x) <- value replacement-Version von mode(): Setzen des Modus von x
is.character(x) Feststellen, ob x den Modus "character" besitzt
nchar(x) Gibt die Anzahl der Zeichen in der Zeichenkette x an (Maskierungs-Zeichen werden nicht gezählt)
str(object) Ausgabe der Struktur von object (Modus und Wert)
?Quotes Dokumentation: Weitere Informationen im package base unter Quotes

Die folgende Tabelle beschreibt die Rückgabewerte der Funktionen und den Datentyp des Rückgabewertes:

Funktion Rückgabewert Datentyp
mode(x) Modus von x Zeichenkette
mode(x) <- value kein Rückgabewert, dient zum Setzen des Modus von x kein Rückgabewert (replacement form von mode())
is.character(x) TRUE oder FALSE je nach Modus von x "logical"
nchar(x) Anzahl der Zeichen in der Zeichenkette x Zahl mit Speicher-Modus "integer"
str(object) Kein Rückgabewert, nur Ausgabe der Struktur eines Objektes. "NULL": kein Rückgabewert

Typumwandlungen (coercion)

Die folgende Tabelle listet die oben besprochenen Typumwandlungen auf; welche Typumwandlungen tatsächlich durchgeführt werden können, wird hier nicht nochmals diskutiert.

Funktion Beschreibung
as.character(x) Erzwingt die Umwandlung von x in eine Zeichenkette (Modus "character")
as.logical(x) Erzwingt die Umwandlung von x in einen logischen Wert (Modus "logical")
as.numeric(x) Erzwingt die Umwandlung von x in eine Zahl (Modus "numeric")

Die folgende Tabelle beschreibt die Rückgabewerte der Funktionen und den Datentyp des Rückgabewertes:

Funktion Rückgabewert Datentyp
as.character(x) Zeichenkette (oder ein Zeichen), die aus dem Objekt x gebildet wird Zeichenkette ("character")
as.logical(x) Logischer Wert, der aus dem Objekt x gebildet wird logischer Wert ("logical")
as.numeric(x) Zahl, die aus dem Objekt x gebildet wird Zahl ("numeric")