30
Nationale Besonderheiten

Dieses Kapitel behandelt die folgenden Themen:

Image       Länderspezifische Zeichenformate einstellen

Image       Zeichensätze und -codierung

Image       Geld-, Datums- und Zahlenformate

Image       Konstruktion eines eigenen Formats

Die Klasse locale (Header <locale>) bestimmt die nationalen Besonderheiten von Zeichensätzen. Dazu gehören Sonderzeichen wie die deutschen Umlaute oder Zeichen mit Akzent wie in den Worten señor und garçon. Die Ordnung zum Vergleich von Zeichenketten wird dadurch definiert, das heißt zum Beispiel, ob ä unter a oder unter ae einsortiert wird, und das Erscheinungsbild für die Ein- und Ausgabe numerischer Größen (Dezimalpunkt oder -komma?) und Datumsangaben (31.1.2015 oder 1/31/2015). Die verschiedenen Kategorien werden in Facetten (englisch facets) unterteilt. Dieses Kapitel konzentriert sich auf die häufigsten Anwendungen. Es ist möglich, eigene Facetten zu schreiben, die von den vorhandenen abgeleitet sind. Einzelheiten sind [ISOC++] zu entnehmen.

30.1 Sprachumgebung festlegen und ändern

Ein locale-Objekt wird von der Iostream-Bibliothek benutzt, damit die üblichen nationalen Gepflogenheiten bei der Ein- und Ausgabe eingehalten werden. Wenn von der deutschen Schreibweise von Zahlen auf die angloamerikanische umgeschaltet werden soll, wird das locale-Objekt des Ein- oder Ausgabestroms entsprechend ausgewechselt. Das geschieht mit der Funktion imbue(), der als Parameter ein locale-Objekt übergeben wird. Das englische Wort imbue bedeutet etwa »erfüllen (mit)« oder »inspirieren (mit)«.

Das Setzen der globalen Sprachumgebung, die normalerweise durch Abfrage der Betriebssystemumgebungsvariablen LANG voreingestellt wird, wirkt sich nicht auf existierende Streams wie cin oder cout aus, nur auf neu erzeugte – deswegen muss ggf. imbue() angewendet werden. Falls LANG nicht definiert ist, wird automatisch die C-Sprachumgebung gesetzt, auch classic genannt (siehe Beispiel unten). POSIX (= Portable Operating System Interface for uniX) ist eine Familie von Standards für Betriebssystemschnittstellen. Anstatt POSIX kann eine von vielen anderen Umgebungen gewählt werden, von denen einige hier aufgelistet sind:

de_DE

= Deutsch für Deutschland (ISO 8859-1)

de_DE@euro

= Deutsch für Deutschland mit Euro-Zeichen (ISO 8859-15)

de_DE.utf8

= Deutsch für Deutschland (Unicode UTF-8)

de_CH

= Deutsch für die Schweiz

en_GB

= Englisch für Großbritannien

en_US

= amerikanisches Englisch

es_SV

= Spanisch für El Salvador

Auf Linux- und macOS-Systemen kann das eingestellte Locale mit locale angezeigt werden. locale -a listet alle verfügbaren Locales auf.

cout.imbue(locale::classic());

setzt die Standardausgabe auf die C-Sprachumgebung zurück.

Image

Hinweis

Im Folgenden werden Objekte des Typs locale benutzt, um zum Beispiel eine deutsche Schreibweise für ein Datum einzustellen. Leider sind die Konventionen für die Namensgebung systemabhängig. Die g++-Compiler für Linux und macOS unterstützen diverse Sprachen, ihr Gegenstück für Windows, der MinGW-C++-Compiler, leider nicht! Es gibt also eine Fehlermeldung bei der Ausführung. MinGW kann leider nur die Umgebungen "C" und "". Um festzustellen, welche Locales (wie de_DE und en_US) unterstützt werden, können Sie das folgende Programm checklocales.cpp abwandeln und nutzen.

Image

30.1.1 Die locale-Funktionen

Image       locale()
Konstruktor, der eine Kopie des aktuellen globalen locale-Objekts (gegebenenfalls mit global() eingestellt, siehe oben) erzeugt

Image       explicit locale(const char* name)
explicit locale(const string& name)
Konstruktor. Das übergebene Argument kann zum Beispiel "de_DE.utf8" sein.

Image       locale(const locale& other, const char* name, category cat)
locale(const locale& other, string name, category cat)

Der Konstruktor kopiert other mit Ausnahme der Facetten, die in cat definiert sind. cat kann zum Beispiel (monetary | numeric) sein. Sie werden entsprechend name gewählt. Zum Typ category siehe
Abschnitt 30.4.

Image       locale(const locale& loc1, const locale& loc2, category cats)
konstruiert ein locale-Objekt mit allen Facetten aus loc1 mit Ausnahme derjenigen, die cats implementieren und welche stattdessen aus loc2 genommen werden.

Image       template<class Facet> locale(const locale& other, Facet *f)
Der Konstruktor kopiert alle Facetten aus other mit Ausnahme derjenigen des Typs Facet und installiert *f als Facette, falls f ungleich null ist. Falls f gleich null ist, ist das erzeugte Objekt eine Kopie von other.

Image       const locale& operator=(const locale& rechts) Zuweisungsoperator

Image       bool operator==(const locale& other) const Gleichheitsoperator

Image       template<class Facet> locale combine(const locale& other)
gibt eine Kopie von *this zurück, wobei aber die Facette Facet durch die entsprechende von other ersetzt wird.

Image       string name() const
gibt den Namen des locale-Objekts zurück, falls definiert. Andernfalls wird »*« zurückgegeben.

Image       bool operator()(const string& s1, const string& s2) const
gibt s1 < s2 zurück. Damit kann leicht zum Beispiel ein Vektor v entsprechend den nationalen Zeichenvergleichsregeln, die in einem locale-Objekt loc festgelegt sind, sortiert werden (Beispiel siehe Seite 719).

Image       static locale global(const locale& loc)
setzt das globale locale-Objekt. Der vorherige Wert wird zurückgegeben.

Image       static const locale& classic()
gibt ein locale-Objekt für die C-Sprachumgebung zurück (entspricht locale("C")).

Image       template<class Facet> const Facet& use_facet(const locale& loc)
gibt die Referenz der Facette des Typs Facet des locale-Objekts loc zurück. Falls eine Facette dieses Typs in loc nicht existiert, wird eine bad_cast-Exception ausgeworfen.

Image       template<class Facet> bool has_facet(const locale& loc)
gibt zurück, ob eine Facette des Typs Facet in loc existiert.

30.2 Zeichensätze und -codierung

Ein Zeichensatz ist eine Abbildung von Bitmustern auf Zeichen. Damit Sender und Empfänger sich beim elektronischen Datenaustausch verstehen, ist eine Konvention über die Bedeutung der Bitmuster unerlässlich. Die bekannteste ist ASCII (American Standard Code for Information Interchange). Die ASCII-Tabelle definiert die ersten 7 Bits1 eines Bytes, also 128 Zeichen (siehe Anhang A.1). Umlaute und andere europäische Sonderzeichen sind nicht enthalten, sodass nach und nach viele weitere Zeichensätze hinzukamen. Sehr bekannt ist ISO 8859-1, auch »Latin 1« genannt. Er stimmt in den ersten 128 Bytes mit ASCII überein und definiert in den anderen 128 Bytes Umlaute wie ä, ö, ü und weitere Sonderzeichen, sodass ISO 8859-1 mehr als 20 Sprachen abdeckt. ISO 8859-15, auch »Latin 9« genannt, ist eine Modifikation, die auch das Euro-Zeichen e enthält.

ISO 8859-1 hat den großen Vorteil, dass für die Darstellung eines Zeichens ein Byte ausreichend ist. Wenn allerdings noch arabische, chinesische, japanische und koreanische Zeichen gefragt sind, reicht ein Byte nicht. Aus diesem Grund wurde Unicode [Unic] entwickelt (siehe auch UTF im Glossar, Seite 985). Unicode hat den Anspruch, jedem Zeichen eine Codierung zuzuordnen. Wegen der Fülle der möglichen Zeichen müssen viele Zeichen als Multi-Byte-Sequenzen abgebildet werden. Es gibt mehrere Unicode-Schemata; das am weitesten verbreitete ist UTF-8 (8-Bit Unicode Transformation Format). Jedes UTF-8-Byte hat diese Eigenschaften:

Image       Falls das höchste Bit eines Bytes 0 ist, handelt es sich bei dem Byte um ein ASCII-Zeichen. Die anderen 7 Bits definieren den ASCII-Wert. Die ersten 128 UTF-8-Zeichen stimmen daher mit dem ASCII überein.

Image       Falls das höchste Bit 1 ist, ist das Byte Teil einer Multi-Byte-Sequenz.

Image

Merke:

Eine Zeichenkette kann nur in Kenntnis der verwendeten Codierung sinnvoll bearbeitet und interpretiert werden.

Image

Das bedeutet auch, dass Tastatur, Editor und Anzeige in der Codierung übereinstimmen müssen. Sie haben sicher schon den Effekt bemerkt, dass Umlaute, die ein Programm auf der Konsole ausgibt, nicht korrekt dargestellt werden. Die Ursache ist die fehlende Übereinstimmung der Codierung. In Linux wird die Codierung des Betriebssystems durch die Variable LANG (für language) eingestellt. Oft ist de_DE.UTF-8 üblich. Der de_DE-Anteil sorgt dabei für die hier übliche Dezimalpunkt- und Datumsdarstellung usw. Diese Aspekte werden Facetten genannt; Einzelheiten folgen.

Einstellung der Konsole auf UTF-8

Windows: Durch Anklicken des Fenstersymbols ganz oben links im Eingabeaufforderungsfenster geht man über »Eigenschaften« zu den Schriftarten. Dort Lucida Console wählen. Dann chcp 65001 eintippen.

Linux: Normalerweise ist die Konsole auf UTF-8 eingestellt, siehe Ausgabe des Befehls locale. Falls etwas anderes gewünscht ist: Im Konsolenfenster oben »Einstellungen/Bearbeiten« anklicken.

macOS: Normalerweise ist die Konsole auf UTF-8 eingestellt, siehe Ausgabe des Befehls locale. Falls etwas anderes gewünscht ist: Oben bei geöffneter Konsole den Reiter »Terminal« anklicken, dann »Profile« und bei »Textcodierung« UTF-8 einstellen. Das nächste neu geöffnete Terminal-Fenster hat die neue Codierung.

C++-Zeichenliterale

»Normale« Zeichenliterale werden durch Hochkommas gekennzeichnet, zum Beispiel ’z’. Anstelle des Zeichens z können alle anderen Zeichen des Basiszeichensatzes, der normalerweise in etwa ASCII entspricht, verwendet werden, außer dem Hochkomma selbst wegen der Begrenzerfunktion, dem Backslash und dem Zeilenendezeichen. Mit dem Backslash werden Sonderzeichen eingeleitet, wie die Tabelle 1.5 auf Seite 55 zeigt. Es gibt aber weitere Möglichkeiten, die durch ein Präfix markiert werden. So repräsentieren U und u definierte Zeichensätze.

Image       u8’z’ : u8 vor ’z’ kennzeichnet das Zeichen als ein Zeichen des Typs char8_t, das der UTF-8-Codierung entspricht. Die ersten 128 UTF-8-Zeichen stimmen mit ASCII überein. u8 garantiert eine ASCII-Codierung auch auf Nicht-ASCII-Systemen. Mehrere Bytes, etwa zur Darstellung von Sonderzeichen, sind nicht erlaubt.

Image       u’z’ : Ein vorangestelltes u besagt, dass das Literal den Typ char16_t hat. Es muss mit 16 Bits darstellbar sein und sein Wert wird durch ISO 10646 definiert. ISO 10646 ist eine Norm für den Universal Character Set (UCS), der die Unterformate UCS-2 und UCS-4 hat, entsprechend einer Codierung in 2 bzw. 4 Bytes. Das Standardisierungsgremium für ISO 10646 arbeitet mit dem Unicode-Gremium zusammen, um die verschiedenen Definitionen nicht auseinanderlaufen zu lassen. So entspricht UCS-4 UTF-32.

Image       U’z’ : Ein vorangestelltes U ist dementsprechend ein char32_t-Zeichen.

Image       L’z’ : Ein großes L steht für ein wchar_t-Zeichen. Das »w« steht für »wide«. Dieser Typ ist implementationsabhängig und für Zeichensätze gedacht, die nicht mit einem Byte pro Zeichen auskommen. Die 1-Byte-Zeichen heißen im Gegensatz dazu »narrow characters«.

C++-Stringliterale

Den C++-Zeichenliteralen stehen entsprechende Stringliterale gegenüber:

"ASCII- oder ISO 8859-1-String" L"String mit wchar_t-Zeichen" u8"char8_t codierter String" u"char16_t codierter String" U"char32_t codierter String" R"("roher"String)"

Wenn das Programm im ISO 8859-1-Format abgespeichert wird, werden im folgenden Beispiel die den Umlauten entsprechenden Zahlenwerte ausgegeben:

Die Konsole muss natürlich auch auf ISO 8859-1 eingestellt sein. Die Anweisung s[i] = toupper(s[i], dt); hätte auch durch s[i] = toupper(s[i]);, also Aufruf der entsprechenden C-Funktion ersetzt werden können, weil die locale-Einstellung vorher auf de_DE gesetzt worden ist. Die aktuelle Einstellung wird von toupper(char) berücksichtigt. Die ISO 8859-1-codierte Zeile

string s("ÄÖÜäöüß");

könnte durch

string s("\xC4\xD6\xDC\xE4\xF6\xFC\xDF");

ersetzt werden. Das wäre zwar von jedem Editor lesbar, egal mit welcher Codierungseinstellung. Aber portabel wäre auch das nicht, denn UTF-8-codiert sehen die Umlaute so aus:

string s("\xC3\x84\xC3\x96\xC3\x9C\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F");
Image

Merke:

Ein Programm, das Zeichenketten mit Nicht-ASCII-Zeichen enthält, ist nicht portabel.

Image

Unter portabel wird hier verstanden, dass der Quellcode auf ein beliebiges anderes System übertragen und dort übersetzt werden kann und dass das Programm unabhängig von der locale-Einstellung und der Umgebung dasselbe Ergebnis liefert. Im Folgenden sehen Sie ein Beispiel, das UTF-8-codiert ist und einen wchar_t-String benutzt.

Die Konsole muss natürlich auch auf UTF-8 eingestellt sein, um eine lesbare Anzeige zu bekommen. Bemerkungen zu diesem Beispiel:

Image       Bei einem auf UTF-8 eingestellten Editor wird der Quellcode in UTF-8 gespeichert. Nicht-ASCII-Zeichen werden also Multibyte-Sequenzen (Zeile 11).

Image       Mit wcout, der cout-Entsprechung für wchar_t-Zeichen, wird der wstring ws auf der Konsole ausgegeben (Zeile 13).

Image       Die interne Darstellung eines wstrings im ausführbaren Programm ist ein Array mit n wchar_t-Zeichen, wobei n die Anzahl der Zeichen (nicht der Bytes!) ist, wie sie in Zeile 14 angezeigt wird.

Image       wchar_t ist ein int-Typ. Seine Größe wird in Zeile 15 ausgegeben (auf meinem System 4 Byte).

Image       Die folgende Ausgabe wird in eine Datei des Typs wofstream umgeleitet (Zeilen 16 bis 20).

Image       Die Funktion toupper() (Zeile 22) funktioniert zeichenweise unter Berücksichtigung der übergebenen locale-Einstellung.

Wie man sieht, ist die Arbeit mit Wide-Strings nicht so komfortabel wie mit »normalen« Strings. Auch wird die Umwandlung von Strings verschiedener Codierungen, wie es das Programm iconv (siehe Tipp) leistet, noch kaum unterstützt.

Image

Tipp

Der Befehl iconv (Windows: MinGW-Version von iconv) wandelt Dateiformate um. Aufruf zum Beispiel: iconv -f ISO-8859-1 -t UTF-8 -o utf8.txt deDE.txt

Image

Dabei bedeuten -f (from) das Quellformat, -t (to) das Zielformat. Nach -o folgt der Name der Ausgabedatei; zuletzt der Name der zu konvertierenden Datei. iconv -l zeigt alle dem Programm bekannten Zeichensatzcodierungen an. Dabei sind einige unter mehreren Namen aufgeführt. Zum Beispiel sind die ISO-10464 UCS-Codes dasselbe wie UTF. Das Wort »bekannt« bedeutet in diesem Zusammenhang nicht, dass von jeder Codierung zu jeder anderen konvertiert werden kann.

30.3 Zeichenklassifizierung und -umwandlung

Die Definition, ob zum Beispiel ein spezielles Zeichen ein Buchstabe oder etwas anderes ist, hängt von der Sprache ab. Aus diesem Grund gibt es die Funktionen der Tabellen 33.1 und 33.2 (Seite 952 f.) in einer sprachumgebungsabhängigen Variante für verschiedene Zeichentypen charT:

30.4 Kategorien

Locale-Sprachumgebungen enthalten verschiedene Kategorien, die in Facetten unterteilt sind. Das abfragbare Datum locale::category ist eine int-Bitmaske, die die Oder-Verknüpfung aller oder eines Teils der folgenden Konstanten ist: none, ctype, monetary, numeric, time und messages. Jede dieser Kategorien definiert eine Menge lokaler Facetten, wie die Tabelle 30.1 zeigt. Die Facetten sind Template-Klassen, die als Argument den Typ char oder wchar_t für wide characters haben können.

Tabelle 30.1: Auswahl von Kategorien und Facetten (charT-Template-Typ)

Kategorie

Facetten

Zweck

collate

collate<charT>

Zeichenvergleich

ctype

ctype<charT>

Zeichenklassifizierung

numeric

numpunct<charT>

Zahlenformatierung

num_get<charT>

Eingaben

num_put<charT>

Ausgaben

monetary

moneypunct<char,
Intl = false>

Währungsformatierung (Intl = true für internationale Festlegungen)

money_get<charT>

Eingaben

money_put<charT>

Ausgaben

messages

messages<charT>

Strings aus Message-Katalogen holen

30.4.1 collate

Die Klasse template<typename charT> class collate ist eine Facette, die die Funktionen für Vergleiche von Zeichenketten kapselt. Sie besitzt die folgenden öffentlichen Elementfunktionen:

Image       int compare(const charT* low1, const charT* high1,

const charT* low2,const charT* high2) const

Diese Funktion vergleicht zwei Zeichenketten, die durch die Intervalle [low1, high1) und [low2, high2) definiert werden (zur Definition von Intervallen siehe Seite 980). Es wird 1 zurückgegeben, falls die erste Zeichenkette größer als die zweite ist, –1 im umgekehrten Fall und 0 bei Gleichheit. Der Operator locale::operator()() von Seite 914 ruft diese Funktion auf. Das Beispiel auf Seite 719 zeigt, wie ein locale-Objekt zur sprachlich korrekten Sortierung eingesetzt wird.

Image       basic_string<charT> transform(const charT* low, const charT* high) const
gibt das String-Äquivalent des Bereichs zurück, wobei die Ordnungsrelationen erhalten bleiben, d.h. ein Vergleich zweier erzeugter Strings mit dem Algorithmus lexicographical_compare (siehe Seite 755) muss zum selben Ergebnis wie compare() führen.

Image       long hash(const charT* low, const charT* high) const
gibt einen Hash-Wert für den übergebenen Bereich zurück. Dabei ist gewährleistet, dass der Hash-Wert auch bei unterschiedlichen Werten im Bereich stets derselbe ist, wenn nur compare() die Bereiche als gleich ansieht, d.h. 0 zurückgibt. Ein Beispiel dafür könnte sein, dass ä und ae bei einer Sortierung gleich behandelt werden sollen.

30.4.2 ctype

Die Klasse template<typename charT> class ctype kapselt die Funktionen für Zeichenklassifizierung und -umwandlung. Es existiert eine Spezialisierung ctype<char>. So gibt zum Beispiel der Aufruf der Funktion toupper(c, loc) von Seite 920 für Zeichen des Typs char nichts anderes als use_facet<ctype<char> >(loc).toupper(c) zurück. ctype erbt von der Basisklasse ctype_base, die eine Bitmaske mask für Klassifizierungszwecke nach [ISOC++, category.ctype] etwa wie folgt definieren könnte:

Der Typ T und damit mask muss ein Typ sein, der Bitoperationen erlaubt, also etwa int oder ein enum. Die öffentliche Schnittstelle enthält die folgenden Methoden:

Image       bool is(mask m, charT c) const gibt zurück, ob c zur Klassifizierung m passt.

Image       const charT* is(const charT* low, const charT* high, mask* carr) const
Diese Funktion berechnet einen Wert vom Typ ctype_base::mask für jedes der Zeichen im Intervall [low, high) und legt das Ergebnis im C-Array carr beginnend an der Stelle vec[0] ab. high wird zurückgegeben.

Image       const charT* scan_is(mask m, const charT* low, const charT* high) const
gibt einen Zeiger auf das erste Zeichen im Intervall [low, high) zurück, das der Klassifizierung m genügt. Existiert kein solches Zeichen, wird high zurückgegeben.

Image       const charT* scan_not(mask m, const charT* low, const charT* high) const
gibt einen Zeiger auf das erste Zeichen im Intervall [low, high) zurück, das nicht der Klassifizierung m genügt. Existiert kein solches Zeichen, wird high zurückgegeben.

Image       charT toupper(charT c) const
gibt den entsprechenden Großbuchstaben zurück, sofern ein solcher existiert. Andernfalls wird das Argument zurückgegeben.

Image       const charT* toupper(charT* low, const charT* high) const
verwandelt alle Zeichen im Bereich [low, high) in Großbuchstaben, sofern solche existieren. Es wird high zurückgegeben.

Image       charT tolower(charT c) const
gibt den entsprechenden Kleinbuchstaben zurück, sofern ein solcher existiert. Andernfalls wird das Argument zurückgegeben.

Image       const charT* tolower(charT* low, const charT* high) const
verwandelt alle Zeichen im Bereich [low, high) in Kleinbuchstaben, sofern solche existieren. Es wird high zurückgegeben. Im
Listing 30.3 von Seite 917 könnte der String s unter Verwendung des locale-Objekts dt wie folgt umgewandelt werden:

for (size_t i = 0; i < s.length(); ++i) { s[i] = std::use_facet<std::ctype<charT> >(dt).tolower(s[i]); }

Der Vorspann use_facet<ctype<char> >(dt) gibt die Facette ctype des locale-Objekts dt zurück, deren Funktion tolower() dann aufgerufen wird.

Image       charT widen(char c) const
wandelt c in eine entsprechende Repräsentation des Typs charT um (z.B. wide character wchar_t).

Image       const char* widen(const char* low, const char* high, charT* to) const
wandelt jedes Zeichen im Intervall [low, high) in eine entsprechende Repräsentation des Typs charT um und legt das Ergebnis in to ab. Der Rückgabewert ist high.

Image       char narrow(charT c, char dfault) const
wandelt c in eine entsprechende Repräsentation des Typs char um, falls eine solche existiert. Andernfalls wird dfault zurückgegeben.

Image       const charT* narrow(const charT* low, const charT* high, char vorgabe,

char* to) const

wandelt jedes Zeichen im Intervall [low, high) in eine entsprechende Repräsentation des Typs char um, falls eine solche existiert. Andernfalls wird vorgabe genommen. Das Ergebnis wird in to abgelegt. Der Rückgabewert ist high.

30.4.3 numeric

Die Template-Klassen num_get und num_put wickeln das formatierte Einlesen bzw. die formatierte Ausgabe ab. Sie werden intern von den Standard-Iostreams benutzt, um Zahlen mit national bedingten Dezimal- und Tausendermarkierungen richtig zu bearbeiten, und sind für normale Fälle wohl kaum von Bedeutung, da sie versteckt innerhalb des <<- bzw. >>-Operators Anwendung finden, wie das folgende Beispiel zeigt:

Mit den gegebenen locale-Objekten würde die Eingabe 3.456,78 die Ausgabe 3,456.78 bewirken. Die Abfrage der Markierungen und anderer Dinge mit der Klasse numpunct folgt.

numpunct

Die Facette template<class charT> class numpunct hat die folgende öffentliche Schnittstelle:

Image       charT decimal_point() const
gibt den verwendeten Dezimalpunkt zurück (z. B. einen Punkt für en_US oder ein Komma für de_DE).

Image       charT thousands_sep() const
gibt das Trennzeichen zwischen Tausender-Gruppen zurück.

Image       string grouping() const
Die Zeichen des zurückgegebenen Strings, im Folgenden str genannt, sind als ganzzahlige Zahlen zu interpretieren, die die Anzahl der Ziffern in der Gruppe angeben, beginnend mit Position 0 als am weitesten rechts stehende Gruppe. Wenn str.size() <= i für eine Position i gilt, ist die Zahl dieselbe wie die für Position i-1. Zum Beispiel wird die Anzahl der Ziffern einer Tausendergruppe gleich 3 sein, d.h. S == "\03". Negative Zahlen charakterisieren unbegrenzte Gruppen wie etwa Zahlen ganz ohne Markierung der Tausender (Beispiel siehe cppbuch/k30/numpunct.cpp).

Image       basic_string<charT> truename() const und
basic_string<charT> falsename() const
geben den verwendeten Namen (true bzw. false) für die Ausgabe zurück, sofern boolalpha == true ist (vgl.
Abschnitt 9.4, Seite 434).

Die Klasse kann für ein bestimmtes locale-Objekt wie folgt benutzt werden:

30.4.4 monetary

Diese Kategorie enthält alles, was für die formatierte Ein- und Ausgabe von Geldbeträgen einschließlich der Währungsangaben gebraucht wird. In den folgenden Beispielen wird von einer einfachen Klasse Geld ausgegangen.

moneypunct

Die Facette template<class charT> class moneypunct definiert Währungssymbole und die Formatierung. Sie erbt von der Klasse money_base, die die öffentlichen Elemente

enum part { none, space, symbol, sign, value }; struct pattern { char field[4]; };

bereitstellt. Ein monetäres Format wird durch eine Folge von vier Komponenten spezifiziert, die in einem pattern-Objekt p zusammengefasst werden. Das Element static_cast< part>(p.field[i]) bestimmt die i-te Komponente des Formats. Aus Effizienzgründen ist field vom Typ char anstatt vom Typ part. Im Feld eines pattern-Objekts kann eines der Elemente von part genau einmal vorkommen. Die Klasse money_punct hat die folgende öffentliche Schnittstelle:

Image       charT decimal_point() const gibt den verwendeten Dezimalpunkt zurück.

Image       charT thousands_sep() const
gibt das Trennzeichen zwischen Tausender-Gruppen zurück.

Image       string grouping() const Bedeutung wie bei numpunct (Seite 923).

Image       basic_string<charT> curr_symbol() const
gibt das Währungssymbol zurück, z.B. $. Für internationale Instanziierungen (vgl. Tabelle 30.1, Seite 920) werden im Allgemeinen drei Buchstaben und ein Leerzeichen zurückgegeben, z.B. »USD «.

Image       basic_string<charT> positive_sign() const und
basic_string<charT> negative_sign() const
geben das Zeichen für einen positiven Wert (+ oder Leerzeichen) bzw. einen negativen Wert (-) als String zurück.

Image       int frac_digits() const
gibt die Ziffern nach dem Dezimalpunkt an, im Allgemeinen zwei.

Image       pattern pos_format() const und
pattern neg_format() const
geben das benutzte Formatierungsmuster zurück. Das Standardmuster ist {symbol, sign, none, value}.

Die Klasse kann für ein bestimmtes locale-Objekt wie folgt benutzt werden:

locale loc; // Kopie des aktuellen globalen locale-Objekts char dezPunkt = use_facet<moneypunct<char> >(loc).decimal_point();

money_get

Die Klasse template<class charT> class money_get wickelt das formatierte Einlesen von Geldbeträgen, ggf. mit Währungsangaben, ab. Sie hat zwei öffentliche Methoden

Image       iter_type get(iter_type s, iter_type end, bool intl, ios_base& f,

ios_base::iostate& err, long double& units) const

Image       iter_type get(iter_type s, iter_type end, bool intl, ios_base& f

ios_base& f, ios_base::iostate& err, string_type& units) const

iter_type ist eine öffentliche, in der Klasse definierte Typbezeichnung für einen Input-Iterator, dessen Typ mit istreambuf_iterator<charT> vorgegeben ist. Dieser Typ wird nicht weiter beschrieben, weil erstens der Typ über den Namen money_get::iter_type benutzbar ist und er zweitens im Allgemeinen nicht alleinstehend benötigt wird, wie das Beispiel unten zeigt. string_type ist der in der Klasse definierte Name für den Typ basic_string<charT>. Diese Methoden lesen einen Geldbetrag als double-Zahl bzw. einen String ein, wobei der Dezimalpunkt eliminiert wird. Sie können in einer benutzerdefinierten Klasse zur Implementierung des Eingabeoperators (>>) verwendet werden. Zurückgegeben wird ein Iterator, der auf das unmittelbar nach dem letzten gültigen Zeichen eines Geldbetrags folgende Zeichen verweist. Im folgenden Beispiel wird Bezug auf die oben erwähnte Klasse Geld genommen. Die Funktion getloc() gibt die mit dem Stream assoziierte locale zurück.

Zwar ist die Basis der Klasse Geld ein ganzzahliger Cent-Betrag, die obige put()-Funktion verlangt jedoch long double. Aus diesem Grund wird die Typumwandlung static_cast eingesetzt. Das Beispiel zeigt, dass der Istream is an die Stelle des verlangten Input-Iterators treten kann. Der Grund liegt darin, dass die Klasse istreambuf_iterator<charT> einen Konstruktor hat, der ein istream-Objekt als Parameter nimmt. Der vierte Parameter von get() nutzt aus, dass die Klasse istream von der Klasse ios_base erbt. Er dient dazu, intern über getloc() auf die Facette moneypunct zuzugreifen. Das folgende Programmfragment zeigt eine Anwendung:

Geld dollars; cin.imbue(locale("en_US")); cin >> dollars;

Eine Zeichenfolge "1056.23" im Eingabestrom führt zu dem Ergebnis

dollars.betrag == 105623.

money_put

Die Template-Klasse template<class charT> class money_put wickelt die formatierte Ausgabe von Geldbeträgen, gegebenenfalls mit Währungsangaben, ab. Sie hat zwei öffentliche Methoden:

Image       iter_type put(iter_type s, bool intl, ios_base& f,

charT fill, long double& units) const

Image       iter_type put(iter_type s, bool intl, ios_base& f,

charT fill, string_type& digits) const

Die Typbezeichnungen entsprechen denen der Klasse money_get, wobei der Typ iter_type natürlich ein Output-Iterator ist. Anwendungsmöglichkeiten ergeben sich in Analogie zur Klasse money_get, zum Beispiel der Ausgabeoperator für die obige Klasse Geld:

Listing 30.12: locale-abhängiger Ausgabeoperator der Klasse Geld (Fortsetzung von Listing 30.11)

std::ostream& operator<<(std::ostream& os, const Geld& geld) { std::ostream::sentry s(os); // Ein sentry-Objekt bereitet den Stream vor. os.setf(std::ios::showbase); // Damit die Währung angezeigt wird. if (s) { std::use_facet<std::money_put<char> >(os.getloc()) .put(os, true, os, ’ ’, static_cast<double>(geld.getBetrag())); } return os; }

30.4.5 messages

Die Klasse template<class charT> class messages implementiert das Holen von Meldungen aus Katalogen, ähnlich wie das GNU-Programm gettext()2. Wie ein Katalog realisiert ist, ob zum Beispiel als Datei oder Teil einer Datenbank, ist implementationsabhängig. Der Typ catalog, ein int-Typ, steht für eine Katalognummer. Es gibt die folgenden Elementfunktionen:

Image       catalog open(const string& fn, const locale&) const
eröffnet den Katalog, der durch den String fn identifiziert wird, und gibt eine Identifizierungszahl zurück, die bis zum folgenden close() zu dem Katalog gehört. Falls diese Zahl negativ ist, kann der Katalog nicht geöffnet werden.

Image       void close(catalog c) schließt den Katalog c.

Image       basic_string<charT> get(catalog c, int set, int msgid,
const basic_string<charT>& vorgabe) const

Es wird die durch die Argumente set, msgid und vorgabe identifizierte Meldung zurückgegeben. Falls keine Meldung gefunden wird, ist vorgabe das Ergebnis.

Durch Setzen der Sprachumgebung werden aus dem Katalog die übersetzten Meldungen geholt. Das Programm gibt auf meinem Linux-System aus:

invalid character class -> Ungültige Zeichenklasse %s no syntax specified -> es wurde keine Syntax angegeben unbalanced ( -> ( ohne schließendes Gegenstück write error -> Schreibfehler
30.5 Konstruktion eigener Facetten

Man kann vorhandene Facetten durch eigene ersetzen. Dazu muss man wissen, dass zu allen Methoden der oben beschriebenen Facetten zusätzliche virtuelle Methoden mit exakt denselben Schnittstellen existieren, die ein vorangestelltes do_ im Namen haben und protected sind. Diese Methoden werden von den oben beschriebenen aufgerufen. Ferner gibt es Klassen, die von den beschriebenen Facetten nur die protected-Schnittstelle erben und im Namen ein nachgestelltes _byname tragen, um auszudrücken, dass Namen für die Facetten vergeben werden können. Von diesen Klassen können eigene Klassen abgeleitet und die Methoden überschrieben werden. Das folgende Beispiel zeigt, wie das vorgegebene Standardsymbol für Euro, nämlich EUR, mithilfe einer eigenen Facette für Währungssymbole durch das Symbol e ersetzt wird.

Listing 30.14: Eigene Facette (cppbuch/k30/geld/euro.cpp)

#include "Geld.h" #include <iostream> #include <locale> #include <string> using MeinMoneypunct = std::moneypunct_byname<char, true>; // true für Internationalisierung class MeinWaehrungsformat : public MeinMoneypunct { protected: // Überschreiben der virtuellen Funktion do_curr_symbol(), die von der //public-Funktion curr_symbol() der Basisklasse moneypunct gerufen wird: std::string do_curr_symbol() const override { return wsymbol; } public: MeinWaehrungsformat(const std::locale& loc, const char* ws) : MeinMoneypunct(loc.name().c_str()), wsymbol(ws) {} private: const char* wsymbol; }; using namespace std; int main() { locale locUS("en_US.utf8"); Geld derBetrag; cout << "Eingabe˽in˽Cent(!),˽z.B.˽123456:?"; cin >> derBetrag; cout << "direkte˽Abfrage˽mit˽voreingestellter˽locale˽(" << locale().name() // locale ’C’ << ")˽:" << derBetrag.getBetrag() << ’\n’; // 123456 cout.imbue(locUS); // cout auf enUS umschalten cout << "Es˽wurde˽" << derBetrag << "˽eingegeben˽(US-Format).\n";// USD 1,234.56 locale deDEeuro("de_DE.utf8"); cout << "Ausgabe˽Standard-Währungssymbol˽EUR˽und˽Dezimalkomma˽statt˽" "Dezimalpunkt˽:\n"; // Achtung: KEINE Währungsumrechnung, nur Darstellungsänderung! cout.imbue(deDEeuro); // cout auf deDE@euro umschalten cout << derBetrag << ’\n’; // 1.234,56 EUR MeinWaehrungsformat mwf(deDEeuro, "\u20ac"); // Euro in UTF-8 // \xA4 in ISO-8859-15 cout << "Ausgabe˽eigenes˽Währungssymbol˽und˽Dezimalkomma˽statt˽" "Dezimalpunkt˽:\n"; cout.imbue(locale(deDEeuro, &mwf)); cout << derBetrag << ’\n’; // 1.234,56 e }


1 Es werden wie üblich 8 Bits pro Byte angenommen. Ausnahmen gibt es, aber nur wenige.

2 https://de.wikipedia.org/wiki/GNU_gettext