String-Literale

String-Bearbeitung in C++ ist leider manchmal etwas komplizierter, als man das erwartet. Das Erbe von C wird beispielsweise in den String-Literalen deutlich sichtbar.

Video

Quellcode

#include <iostream>
#include <string>
#include <cstring>

int main()
{
  auto s   =   "So sehen Strings in C/C++ aus. Eigentlich problemlos...";
  auto w   =  L"Das, was früher schon mal Unicode sein sollte.........."; 
  auto u8s = u8"In C++11 kommen UTF-8-Strings dazu mit - äöü - Umlauten";
  auto c16 =  u"Das ganze in UTF-16-Kodierung und - äöü - Umlauten.....";
  auto c32 =  U"Und noch einmal in UTF-32 - sogar mit Linear B: ......\U000100E0";
  
  auto r   =  R"(Raw-Strings. Ganz neu. Sogar mit "Anfuehrungszeichen"..)";
  auto r2  =  R"grenze(Raw-Strings haben waehlbare Begrenzer: "(Siehste?)"...)grenze";
  
  std::cout << "s: " << std::strlen(s) << std::endl;
  std::cout << "w: " << std::wcslen(w) << std::endl;
  std::cout << "sizeof(wchar_t): " << sizeof(wchar_t) << std::endl;

  std::cout << "u8s: " << std::char_traits<char>::length(u8s) << std::endl;
  std::cout << "c16: " << std::char_traits<char16_t>::length(c16) << std::endl;
  std::cout << "c32: " << std::char_traits<char32_t>::length(c32) << std::endl;
  std::cout << "r: " << std::char_traits<char>::length(r) << std::endl;
  
  std::cout << r << std::endl;
  std::cout << r2 << std::endl;
}

Erklärung

String-Literale in C++ sind grundsätzlich eingefasst von "". Damit könnte die Erklärung schon zuende sein, wenn das Thema einfach wäre. Leider ist es das nicht.

String-Literale in C++ sind nämlich von C geerbt. Der Typ eines solchen Ausdrucks ist ein Array von const char_T, wobei char_T der entsprechende Typ eines einzelnen Zeichens ist. Die Arrays sind nullterminiert, d.h. das letzte Zeichen ist eine Null, um das Ende des Strings anzuzeigen. "Normale" Arrays sind dabei nun vom Typ const char[], der Zeichentyp ist also char. Damit kann man dummerweise nur 8 Bit erfassen. Reicht also gerade mal für ASCII. In Zeiten, als das für C spezifiziert wurde, war das kein Problem. Man merkt der Sprache da eben ihre amerikanische Herkunft an. Mittlerweile ist das aber ein Problem. Man will ja schließlich auch Programme für andere Sprachregionen entwickeln.

Daher kam irgendwann in C und C++ der Präfix L für die Literale hinzu. Ist ein Literal in der Form L"", dann ändert sich der Zeichentyp auf wchar_t. Dieser Typ ist nicht allzu genau spezifiziert, umfasst aber meist 16 oder 32 Bit und kann daher oft UCS-2 oder UCS-4-kodierte Unicode-Strings aufnehmen. Was genau er kann, hängt von der Zielplattform ab: Linux hat 4 Byte, Windows 2 Byte wchar_t.

Mit C++11 kommen nun drei neue Präfixe hinzu, deren Bedeutung etwas genauer festgelegt ist: u8"" für char-Strings in UTF-8-Kodierung, u"" für char16_t-Strings in UTF-16-Kodierung und U"" für char32_t-Strings in UTF-32-Kodierung. Durch die Festlegung der Kodierung weiß man etwas genauer, was einen erwartet. Das Problem ist lediglich, dass nicht alle Teile der Standardbibliothek schon darauf eingerichtet sind. So liefern beispielsweise strlen() und std::char_traits<char>::length() für UTF-8-kodierte Strings falsche Ergebnisse. Das Problem: weiterhin sind das nur Arrays für C, die letztlich keinerlei Information mehr enthalten, was eigentlich in ihnen steckt. Daher bleibt den Funktionen zur Ermittlung der Länge nur die Möglichkeit, die Zeichen bis zum Erreichen der 0 zu zählen. In UTF-8 sind allerdings alle Zeichen oberhalb von ASCII-Code 127 durch 2 Bytes (oder mehr) dargestellt und werden daher mehrfach gezählt. Der Aufruf in Zeile 20 ermittlet daher 58, statt der eigentlich korrekten 55 Zeichen.

Eine besondere Neuerung in C++11 sind Raw-Strings. Durch das Präfix R (welches mit den anderen Typpräfixen kombiniert werden kann) ändern sich die Interpretationsregeln für den Inhalt zwischen "". Normalerweise haben \ und einige andere Zeichen da spezielle Bedeutungen. Diese entfallen. Man kann nun auch normale Anführungszeichen in das Literal einbauen. Erreicht wird das, indem der Begrenzer des Strings quasi selbst festgelegt werden kann. Die Begrenzung hat immer die Form "d-chars(...)d-chars". d-chars bezeichnet hierbei eine beliebige Folge von Zeichen aus dem basic source character set, jenem ominösen Zeichensatz, den jeder C++-Kompiler beherrschen muss. Die Folge )d-chars" sollte dabei im Raw-String nicht vorkommen. Durch die freie Festlegung ist das nicht schwer zu erreichen (siehe Zeile 14).