ZurückZum InhaltVorwärts

Örtliche Anpassung

C ist im Kern eine US-amerikanische Sprache, die von einem US-amerikanischen Standard getragen wird. Trotzdem finden sich im Standard auch andere Einflüsse wieder. Der eine Einfluß kommt aus den Bemühungen, ein portables Betriebssystem zu definieren, das unter dem Namen POSIX bekannt wurde. Der zweite erkennbare Einfluß ist die Herstellervereinigung X/OPEN. Sie ist ein Zusammenschluß primär europäischer Computerhertsller mit dem Ziel, eine Internationalisierung der verwendeten Software zu definieren.

Andere Einflüsse kommen aber z.B. auch aus Japan, die bei der Mitarbeit am C-Standard deutlich gemacht haben, daß dies aus ihrer Sicht der letzte Sprachstandard ist, der nicht alle gesprochenen Sprachen unterstützt.

Der erste Schritt (der selbst heute noch nicht in vielen Systemen durchgängig eingehalten wird) zu einer besseren nationalen Anpaßbarkeit war der Übergang zu einem 8-Bit-Zeichensatz. Ältere Programme haben bisweilen vorausgesetzt, daß innerhalb der Bytes nur sieben Bit relevant für ein Zeichen sind, und haben das achte Bit für eigene Hilfsfunktionen benutzt.

Die neuen Programme bezeichnete man als "8 Bit clean" (sauber in Bezug auf 8 Bits). Das allein genügt aber nicht, um Programme ohne Neuübersetzung auf verschiedene Länder einstellbar zu machen. Die Internationalisierung muß darüber hinaus Zahlenangaben, Zeichensätze, Tastaturlayouts, Bildschirmdarstellungen, Zeitangaben, Ersatzdarstellungen und weiteres mehr berücksichtigen. Dies ist sicher keine leichte Aufgabe und es wird noch Jahre dauern, bis sich diese Fähigkeiten in den meisten Compilern und Betriebssystemen finden.

In den Standard wurden Optionen aufgenommen, die der Internationalisierung dienen. Unter DOS sind es tatsächlich nur Optionen, da bis heute kaum Compiler existieren, die die Internationalisierung implementiert haben. In der UNIX-Welt kann man Compiler mit Internationalisierung am Gütesiegel XPG3 (X/OPEN Portability Guide Version 3) erkennen.

Im Standard heißen Gebiete mit Abweichungen vom US-amerikanischen Kulturkreis "Schauplätze" (locale). Besonderheiten gibt es für den verwendeten Zeichensatz, bei Zahlen und bei den Formatregeln für Zeit- und Währungsdarstellungen. In einigen Implementierungen werden auch Angaben für Ja/Nein Entscheidungen abhängig vom Schauplatz beschrieben. Weiter werden Textkataloge eingeführt. Internationalisierte Programme geben alle Textausgaben aus Katalogen aus, sodaß eine Anpassung durch Änderungen von Textdateien möglich ist.

Internationalisierung im ANSI-Standard

Beginnen wir mit der Darstellung, wie sich der C-Standard die örtliche Anpassung denkt, und sehen uns danach mögliche Anpassungen an, die der Programmierer bei fehlender Internationalisierung selbst vornehmen kann.

Die Informationsdatei "locale.h"

In der Informationsdatei locale.h werden zwei Funktionen zur Internationalisierung, eine Typdefinition und mehrere Makros, beschrieben. Die örtlichen Gegebenheiten des Programmes werden in einer Struktur mit dem Namen lconv beschrieben. Diese Struktur beschreibt die numerische Darstellung der Fließkommazahlen und der Währungszahlen.

#include <locale.h>
char * setlocale (int Kategorie, const char * Schauplatz);
struct lconv * localeconv (void);

Mit setlocale() kann man für ein bestimmtes Gebiet alle oder gezielte Einstellungen setzen oder die momentane Einstellung abfragen. Oft wird aber nur eine einzige Einstellung unterstützt: "C". Mit "C" ist die absolut notwendige Voreinstellung für C-Programme gemeint. Diese Einstellung entspricht natürlich der US-amerikanischen Umgebung.

Als Schauplatz sind zwei Namen immer gültig: "C" und "". Der "C"-Schauplatz sagt, daß sich die Funktionen, die von einem gesetzten Schauplatz abhängig sind, genauso wie bisher verhalten. Ein leerer Text als Schauplatz gibt an, daß die Einstellung so gut als möglich vorgenommen werden sollte. Zumeist werden dann die gesetzten Umgebungsvariablen berücksichtigt (z. B. LANG=de).

Haüfig ist eine Einstellung von "C" mit "" identisch.

Es gibt fünf Kategorien, die einstellbar sein sollten:

LC_ALL Alle Anpassungen übernehmen
LC_COLLATE für strcoll() /strxfrm()
LC_CTYPE für is... (ohne isdigit()/isxdigit())
LC_MONETARY für localeconv()
LC_NUMERIC für printf()/scanf() und str...()
LC_TIME für strftime()

Jede dieser Kategorien kann für einen Schauplatz (locale) getrennt gesetzt werden. Die Namen der Schauplätze werden nach einem festgelegten Schema aus einer Kennung für die Sprache, dem Ländercode und dem verwendeten Zeichencode aufgebaut. Eine mögliche Variante kann noch angegeben werden, um spezielle Unterscheidungen zusätzlich vorzunehmen. Ein Beispiel wäre eine unterschiedliche Sortierfolge (so unterscheiden sich z. B. Duden und Telefonbücher in der Sortierfolge).

Sprache[_Land[.Code]][@Variante]

In Deutschland und Österreich wäre eine typische Angabe für den erweiterten IBM-Zeichensatz der PC's:

de_DE.437

Beispiele für Sprachen:

da, de, en, es, fr, it, no, ru, sv

Dahinter verbergen sich: dänisch, deutsch, englisch, spanisch, französisch, italienisch, norwegisch, russisch und schwedisch.

Als Ländercodes gibt es:

DK, CH, DE, UK, US, ES, CA, CH, FR, IT, NO, RU, SE

Die Abkürzungen sind leicht entschlüsselbar, wenn man als Besonderheit weiß, daß CA für Canada steht.

Unter UNIX ist weltweit ein ganz anderer Zeichensatz verbreitet: der ISO 8859-1 (auch ISO Latin 1 oder Western genannt). Dieser Zeichensatz, der durch die Internationale Standard Organisation festgelegt wurde, umfaßt alle westeuropäischen und amerikanischen Zeichen. Außerdem ist er der Standard-Zeichensatz für das WWW. In weiteren Varianten gibt es diesen Zeichensatz auch für osteuropäische Sprachen sowie für Russisch und Griechisch. Die beiden letzteren definieren jeweils zusätzlich eine englische Darstellung.

Definitionsdateien

Die Informationen für alle Kategorien und Zeichensätze befinden sich in Dateien, die den Namen der Kategorie tragen. Die Dateien stehen in einem Verzeichnis mit dem Namen des Schauplatzes. Und diese wiederum werden in einem Verzeichnis zusammengefaßt.

Mit setlocale() erfolgt die Einstellung. Als Beispiel kann die übliche Voreinstellung dienen. Vor dem Aufruf der main()-Funktion wird häufig die Funktion

setlocale (LC_ALL,"C");

automatisch aufgerufen, um die Grundeinstellungen zu machen. setlocale() liefert als Ergebnis einen Zeiger auf den Namen der Umgebung zurück oder aber NULL. Bei dem geschilderten Aufruf würde ein Zeiger auf den Text "C" zurückgegeben. Dieser Aufruf muß immer funktionieren, da er das Minimum des Standards beinhaltet.

Abfrage des momentan gesetzten Schauplatzes

In einem Programm kann die momentane Einstellung ebenfalls mit Hilfe von setlocale() abgefragt werden. Dazu wird beim Aufruf ein Nullzeiger als Schauplatz übergeben. Der Rückgabewert ist dann ein Zeiger auf den Namen des Schauplatzes.

printf ("\nEingestellt wurde: %s\n",setlocale (LC_ALL,NULL));

Die Abfrage für eine spezielle Kategorie ist natürlich ebenfalls möglich.

Im folgenden werden die einzelnen Anpassungen nacheinander besprochen. Beachten Sie aber, daß nur wenige Compiler mit ihren Entwicklungsumgebungen das alles schon fertig implementiert haben. Sollte der Compiler, den Sie momentan benutzen, dies nicht können, dann wird Ihnen die Besprechung zumindest einen Hinweis auf mögliche Entwicklungen geben.

Vielleicht fragen Sie auch beim Hersteller des Compilers nach, wann er sich den Spielrehgeln von XPG3 anschließt.

Beginnen wir mit der häufigsten Anwendung, den is...()-Funktionen.

Anpassung der is...() Funktionen oder Makros

Die Funktionen oder Makros, die die Zeichenklassifikation durchführen, werden in der Informationsdatei ctype.h beschrieben. In vielen Fällen ist dort ein Feld definiert, dessen Variablen für jedes Zeichen ein Bitmuster enthalten, das ein Zeichen klassifiziert. Will man ein bestimmtes Zeichen bewerten, dann wird der Code des Zeichens als Index in die Tabelle benutzt, und mit einer bitweisen Und-Operation werden das oder die Bits untersucht. In der hier verwendeten Implementierung wurden keine eigenen Bits für die Kategorie "druckbar" und "graphisch" verwendet. Diese Unterscheideung machen dann die Bewertungsfunktionen.

Im Bild 1 gibt das Programm typen1.c für alle möglichen Zeichen den Inhalt der Bwertungstabelle aus. Durch eine Reihe von "if"-Abfragen gibt das Programm zusätzlich eine lesbare Interpretation des Bitmusters aus.

Bild 9-1: Bewertung von Zeichen

Unterstützt die Entwicklungsumgebung einen deutschen Zeichensatz, dann reagieren auch die is...() Makros und Funktionen korrekt. Für den Fall, daß das noch nicht vorhanden ist, kann man sich möglicherweise behelfen. Einen solchen "workaround" (Umgehung eines Problems) zeigen die beiden folgenden Programme.

Sollen die Standardfunktionen auch für die deutschen Zeichen funktionieren, dann braucht man nur in der Tabelle an den entsprechenden Stellen die Klassifizierungsbits zu setzen. Dies wird im Programm typen2.c im Bild 2 gemacht.

Die Konstante CHAR_MAX aus limits.h gibt an, wieviele Zeichen im Zeichensatz benutzt werden. In einer reinen Implementierung für die "C"-Umgebung ist CHAR_MAX gleich 127. In vielen DOS-Implementierung kann CHAR_MAX auch 255 sein. Wenn man sich die Datei limits.h ansieht, dann findet man u. U. eine weitere Unterscheidung. Wird im Compiler eine char-Variable normalerweise mit Vorzeichen verwendet (signed char), dann ist der Zeichenbereich kleiner, als wenn die char-Variablen ohne Vorzeichen verwendet werden (unsigned char).

Die Unterscheidung ob char mit oder ohne Vorzeichen dargestellt wird, kann bei TurboC im Optionenmenü beim Compiler eingestellt werden. Um deutsche Zeichen sauber zu verarbeiten, ist die Einstellung ohne Vorzeichen vorzuziehen.

Für den Fall, daß ein Compiler den ANSI-Standard im Bereich der Schauplätze auf das unbedingte Minimum beschränkt (also "C"), kann der Programmierer möglicherweise die deutschen Umlaute in die vorhandene Vergleichstabelle selber eintragen. Voraussetzung ist aber, das CHAR_MAX den 8-Bit-Bereich angibt (255) (siehe Optionenmenü oder beim Aufruf).

Bild 9-2: Setzen der deutschen Umlaute

Läßt man das Programm im Bild 2 ablaufen, sieht mam möglicherweise zwar die Meldung, daß ein großer oder kleiner Buchstabe existiert, aber trotzdem nicht die Anzeige des Zeichens, da isprint() mit den zusätzlichen Einträgen nicht umgehen kann.

Bild 9-3: Ersetzen von Makros für Umlaute

Bei einer Implementierung, die sich exakt an den ANSI-Standard hält, würden die zusätzlich gesetzten Bits ausrreichen, um alle Testmakros is...() korrekt umzustellen.

Als Abhilfe bietet sich an, extra Bits für Sonderfälle zu definieren, etwa für "druckbar". Eine andere Möglichkeit bieten selbstgeschriebene Makros. Im Bild 3 wurden zwei Makros gelöscht, die in der ctype.h definiert waren, und durch eigene ersetzt.

Die beiden Makros isprint() und isgraph() unterscheiden sich nur in der Handhabung des Leerzeichens. Wenn also ein Zeichen ein Großbuchstabe, ein Kleinbuchstabe, eine Ziffer oder ein Trennzeichen ist, dann ist es auch ein graphisch darstellbares Zeichen. Und wenn es entweder ein graphisch darstellbares Zeichen oder die Leertaste ist, dann haben wir ein druckbares Zeichen vor uns.

Bei der Implementierung ist Vorsicht geboten. Das Zeichen sollte immer in Klammern stehen und die Teilausdrücke auch, um Probleme bei berechneten Zeichen zu vermeiden. Es ist bei unsauber geschriebenen Makros bisweilen problematisch, anstelle von 'u' z. B. ein "'u' +1" anzugeben. Klammern helfen hier.

Vielleicht fällt dem Leser auf, daß beim Zugriff auf die Klassifizierungstabelle mit 257 Einträgen eine Eins auf das Zeichen addiert wird. Die Tabelle soll oft neben den eigentlichen Zeichen auch EOF umfassen, das meist mit (-1) dargestellt wird. Mit der Addition kann EOF als erstes Element bewertet werden.

Trotz der neuen Makros bleibt das Problem der Funktionen is...() ungelöst. Auch wenn Makros definiert wurden, muß es immer auch eine Implementierung als Funktion geben, da es erlaubt ist, auf einen definierten Namen den Adreßoperator anzuwenden oder bei der Verwendung explizit die Funktion auszuwählen.

Als vollständige Lösung müßte man dann eine eigene Bibliothek mit allen Bewertungsfunktionen selbst schreiben und beim Linken stets vor der Standardbibliothek aufrufen. Hat der Linker einen Namen bereits gefunden, wird er ihn in einer weiteren Bibliothek nicht mehr suchen. So gelten die zuerst gefundenen, selbst geschriebenen Funktionen.

Zusätzlich müßte man die Informationsdateien zu dem mit setlocale() gesetzten Schauplatz auswerten. (../meinLocale/LC_CTYPE)

In unserem Fall wollen wir es mit den beiden neuen Makros bewenden lassen.

Setzen der Vergleichsreihenfolge

Bei lexikalischen Vergleichen spielt nicht die Codierung eines Zeichensatzes eine Rolle, sondern die richtige Sortierreihenfolge. Einen Umlaut könnte man im Deutschen direkt hinter das entsprechende Zeichen ohne Umlaut stellen und ein scharfes "ß" hinter das einfache "s".

Um solche Vergleichsreihenfolgen zu setzen, kann (oder könnte) man wieder setlocale() benutzen und als Kategorie LC_COLLATE verwenden (oder LC_ALL).

#include <string.h>
int strcoll(char *text1, char *text2);
int strxfrm (char *puffer, const char *text, int max)

Als neue Vergleichsfunktion wurde im Standard strcoll() definiert, die eine möglicherweise mit LC_COLLATE gesetzte Vergleichsreihenfolge berücksichtigt.

Das Ergebnis ist wie bei anderen Vergleichsfunktionen größer, gleich oder kleiner als null, entsprechend der ähnlichen Vergleichsfunktion strcmp().

Da die Vergleichsinformation nun nicht mehr auf dem absoluten (ASCII-) Code des Zeichens beruht, sondern aus einer Vergleichstabelle gewonnen werden muß, benötigen solche ortsabhängigen Vergleiche einen etwas größeren Verarbeitungsaufwand. Man hat daher zusätzlich eine Transformationsfunktion definiert, die einen ortsabhängigen Text in einen ortsunabhängigen Text umsetzt.

Auf transformierte Texte können dann die schon bisher üblichen Vergleichsfunktionen angewendet werden. Die Idee ist, das bei mehreren Vergleichen des jeweiligen Textes damit eine Zeitersparnis verbunden ist.

Bild 9-4: Internationale Vergleichsfunktion

Als Rückgabewert liefert die Transformationsfunktion die Nettolänge des erzeugten Textes (ohne abschließende '\0').

Die beiden Beispiele zu den lokalen Texten sind etwas formalistisch geraten, da dem Autor kein Compiler zur Verfügung stand, der diese Funktionen korrekt unterstützte.

Besonders interessant dürften diese Funktionen für Multi-Byte Zeichensätze sein.

Bild 9-5: Transformation lokaler Texte

Darstellung von Zahlen und Währungen

Bisher war C immer eine Sprache, die ihre Befürworter im Bereich der Computerindustrie und der technisch-wissenschaftlichen Einrichtungen hatte. Um auch den kommerziellen Bereich besser abzudecken, wurden im Standard Ergänzungen eingeführt, die es leichter machen sollen, Programme unabhängig von der ortsüblichen Darstellung von Zahlen und Währungsbeträgen zu machen.

Die Grundlage für eine ortsübliche Darstellung von Zahlen bildet die Struktur lconv, die in locale.h definiert wird. Der Inhalt dieser globalen Struktur wird mit setlocale() festgelegt, der Ort der Struktur (also deren Adresse) kann mit localeconv() ermittelt werden.

Bild 9-6: Aufbau der Konvertierungsstruktur

Die Einträge in der Struktur sind entweder Zeiger auf Zeichen oder char-Variablen mit Codes. Die Zeiger verweisen auf Texte, die Codes geben Entscheidungen an. Die Texte (mit Ausnahme des Dezimalpunktes) dürfen leer sein, wenn sie in der gewählten Umgebung nicht vorhanden sind. Die Codes sind positiv und können den Wert für CHAR_MAX annehmen, falls sie in der gewählten Umgebung nicht unterstützt werden.

Da der Wert für CHAR_MAX u. U. veränderlich ist (char mit oder ohne Vorzeichen), sollte man bei Überprüfungen auf CHAR_MAX vorsichtig sein.

Bild 9-7: Ausgabe der lokalen Zahleinstellungen

Die Einstellung in der vorgegebenen C-Umgebung sind leere Texte mit der Ausnahme, daß der Zeiger des Dezimalpunktes auf einen Text zeigt, der nur aus einem einzelnen "." besteht. Alle Angaben in den char-Variablen sind auf CHAR_MAX gesetzt, um anzuzeigen, daß sie nicht verwendet werden.

Der Programmierer kann die gewünschten Informationen aus der Struktur lesen und seine Ausgaben oder Eingaben danach ausrichten. Eine Ein- oder Ausgabefunktion für Währungen besteht allerdings nicht.

Die üblichen Ein- und Ausgabefunktionen, wie fprintf() oder fscanf() sollten aber die numerischen Vereinbarungen berücksichtigen. Im Bild 8 wurde der Versuch gemacht, den Dezimalpunkt für die Ausgabe mit printf() auf das bei uns übliche Komma zu setzen. Wohlgemerkt ist dies nur ein Versuch. Die richtigen Werte sollten in der Informationsdatei LC_NUMERIC im richtigen Verzeichnis für den gewählten Schauplatz stehen. Die Strukturvariable sollte aus Sicht eines Programms als konstant angesehen werden.

Bild 9-8: Versuch einer Dezimalpunkteinstellung

Örtliche Zeitangaben

Der letzte der Bereiche, in denen eine Anpassung an ortsübliche Gepflogenheiten vorgesehen ist, ist die Darstellung der Zeit.

size_t strftime(char *puf,int lae,char *format,struct tm *t);

Angepaßt wird hier die Ausgabe der Namen für Tage und Monate. Ähnlich wie bei fprintf() gibt es eine Reihe von Formatelementen, die mit einem "%"-Zeichen eingeleitet werden.

Bild 9-9: Anzeigen der lokalen Zeit

Im Beispiel wurden einige Spezialitäten von TurboC verwendet, um die hier notwendige Vorbesetzung der globalen Variablen timezone, daylight und tzname zu erreichen. Alle drei Variablen sind nicht Teil des ANSI-Standards. Sie werden aber hier benötigt. timezone und tzname finden sich auch unter UNIX. Die Angabe, die hier daylight beinhaltet (Sommerzeit ja/nein), ist auch in der Struktur tm enthalten, die von localtime() geliefert wird.

Arbeiten mit Textkatalogen

Innerhalb eines internationalisierten Programms dürfen keine fest eingebauten Texte verwendet werden. Die Ausgabe von Texten geschieht über Kataloge in Dateien. Die Arbeitsweise entspricht den Funktionen perror() oder strerror(). Hier übergibt man eine Nummer, die in diesem Fall eine Fehlernummer ist, und erhält einen Text zurück.

Um den Aufbau der Kataloge möglichst flexibel gestalten zu können, hat man in XPG3 (aber nicht in ANSI-C) eigene Funktionen definiert.

#include <nl_types.h>
nl_catd catopen (char * name, int oflag);
char * catgets (nl_catd, int set_id, int msg_id, char *def);
int catclose (nl_catd catd);

Die Namen sind, zumindest im Englischen, selbsterklärend. Die benötigte Informationsdatei ist nl_types.h. Vermutlich steht nl dabei für national "languages" (nationale Sprachen). In dieser Datei wird der Datentyp nl_catd definiert sowie die Zugriffsfunktionen auf Kataloge deklariert.

Die Eröffnungsfunktion sucht den gewünschten Katalog und liefert bei Erfolg einen Descriptor für den Katalog. Falls der Name keinen "/" besitzt, sucht die Funktion die Datei in einem Verzeichnis, das durch die Umgebungsvariable NLSPATH definiert wird oder in einem Standardverzeichnis, das die jeweilige Implementierung vorgibt.

Der zweite Parameter wird zur Zeit noch nicht benutzt und sollte immer den Wert "0" haben, um künftige Erweiterungen nicht zu behindern.

Sollte ein Fehler auftreten, wird "-1" zurückgegeben und die zugehörige Fehlernummer in errno gesetzt.

Der zurückgelieferte Descriptor kann in nachfolgenden Aufrufen von den weiteren Funktionen catgets() und catclose() verwendet werden.

Will man eine Meldung aus einem Katalog entnehmen, verwendet man catgets() (catalogue get string / Text aus Katralog holen). Die vier Parameter geben den gewünschten Katalog, den Abschnitt im Katalog, die Textnummer im Abschnitt sowie einen Standardtext an, falls der eigentliche Eintrag nicht gefunden wird.

Da zumindest immer der vorgegebene Standardtext gefunden werden kann, tritt hier kein Fehler auf.

Die letzte Funktion catclose() schließt einen Textkatalog wieder. Bei Erfolg ist das Ergebnis 0.

Die Vorgehensweise und den Aufbau eines Textkataloges kann man wieder am klassischen C-Beispiel, "Hello World", zeigen.

Bild 9-10: Hello World - internationalisiert

Der Aufbau des zugehörigen Kataloges, der unter dem Namen "Hello" im Standardverzeichnis des Systems erwartet wird, sieht wie folgt aus:

$set 1
1 hello, world

Mit $set wird ein Abschnitt eingeleitet, und die eigentliche Meldung erhält zu Verwaltungszwecken eine Nummer, die aber nicht mit ausgelesen wird. Hier ist der Text "hello, world" im Set 1 unter der Meldungsnummer 1 zu finden. Diese Textdateien werden noch mit Hilfe eines Dienstprogramms gencat (generiere Katalog) in eine Form gebracht, auf die schneller zugegriffen werden kann.

Vielleicht vermissen Sie im vorhergegangenen Beispiel die Zeilennummern. Da leider keine vollständige X/OPEN-Implementierung zur Verfügung stand, wurde das Beispiel nicht übersetzt.

Darstellung von Ja/Nein- Antworten

Viele Programme erwarten, daß der Benutzer einfache Ja/Nein- Fragen beantwortet. Die Antworten sind wiederum abhängig von der verwendeten Sprache. XPG3 erwartet von einigen Kommandos (wie rm), daß sie eine länderspezifische Antwort korrekt interpretieren. Obwohl die Kategorie LC_MESSAGES nicht in der Definition des Schauplatzes enthalten ist, kann man bei XPG3-Umgebungen die notwendigen Informationen in einer gleichnamigen Datei (LC_MESSAGES) im Verzeichnis des Schauplatzes mit angeben.

Die Beschreibungsdatei für Ja/Nein-Antworten ist ein einfaches Beispiel für den Aufbau der verwendeten Steuerdateien.

Eine Steuerdatei kann Kommentarzeilen enthalten, die mit "#" beginnen. Am Anfang steht eine Kennung. Sie besteht aus dem Namen der Kategorie, die auch gleichzeitig der Name der Datei ist.

Bild 9-11: Aufbau einer Definitionsdatei

Für die gesuchte Information wurden Schlüsselworte vereinbart. Hier sind die Schlüsselworte yesexpr und noexpr (Ausdruck für Ja oder Nein). Der mögliche Eingabetext wird durch einen regulären Ausdruck beschrieben. Mit regulären Ausdrücken lassen sich Texte beschreiben. Der einfachste RE (für: regular expression) ist ein simples Wort. Kompliziertere Ausdrücke umfassen Metazeichen ("*" oder "?"), die von der Kommandozeile her schon bekannt sind.

Bei weiterem Interesse an den regulären Ausdrücken kann man die Beschreibung des UNIX-Editors ed empfehlen.

Die in der Beispieldatei angegebene Ja-Antwort bedeutet: Akzeptiere als Antwort einen Text, der am Anfang ("^") ein Zeichen aus einer Menge ("[Yy]") erwartet, danach ein beliebiges Zeichen hat (".") und beliebig viele weitere ("*"). Das "*" umfaßt dabei auch die leere Menge. Es kann auch für kein Zeichen stehen.

Steuerung mit Umgebungsvariablen

In der Umgebung eines Programms können Variablen definiert werden, die eine Beschreibung der Standardeinstellung des Schauplatzes beinhalten. Die Namen der Umgebungsvariablen tragen die Namen der Kategorien. Zusätzlich kann eine Umgebungsvariable LC_MESSAGES definiert werden, die den Dateinamen für Mitteilungen enthält. Weiter gibt es eine Variable LANG, die als Standardwert verwendet wird, wenn LC_ALL nicht existiert, aber einzelne andere Umgebungsvariablen LC_.... Die Variable LANG kann mit dem Sprach- und Ortscode vorbelegt werden (LANG=de_DE). In einfachen XPG3 Implementierungen wird die Variable LANG oft auch nur mit einem vereinfachten Ländercode, wie "de" vorbesetzt.

Wurde LC_ALL definiert, erhält sie die höchste Priorität, unabhängig von möglichen abweichenden Einstellungen (LC_ALL=de_DE.437).

Hinweise zum Schreiben internationalisierter Programme

Ein internationalisiertes Programm ist etwas schwieriger zu schreiben als ein Programm, das sich ausschließlich mit ASCII-Zeichen beschäftigt. Einige Spielregeln sollen die Arbeit erleichtern.

Am Anfang eines XPG3-Programms steht eine Makrodefinition:

#define _XOPEN_SOURCE

Für den Fall, daß der Programmierer auch auf die Einhaltung einer standardisierten Betriebssystemschnittstelle achten möchte, sollte ein zweites Makro folgen.

#define _POSIX_SOURCE

Zusammenfassungen

Spezielle Zeichensätze

Das Standardisierungskommitte für ANSI-C ist einer guten Tradition von C gefolgt und hat versucht, das Problem der nationalen Zeichensätze für alle möglichen Gegenden dieser Welt zu lösen. Solange man in einem Schauplatz mit einem 8-Bit-Zeichensatz auskommt, läßt sich das Problem lokaler Sprachen relativ einfach lösen. Schwieriger wird es bei asiatischen Symbolschriften. Hier müssen mehrere Tausende von Bildern kodiert werden können. Letzlich wird vermutlich der 32/16-Bit UniCode das Problem verschiedener Zeichensätze lösen.

Der Standard legt dazu mehrere Funktionen und einen Datentyp fest. Der Datentyp ist wchar_t. Dieser Datentyp ist ganzzahlig und so groß, daß er den größtmöglichen Zeichensatz aller unterstützten Schauplätze aufnehmen kann. Bedingung dabei ist, daß zumindest der ANSI-C-Zeichensatz kodiert werden kann und das Null-Zeichen als Endekennung eines Textes. Dieses Endezeichen soll auch hier einen numerischen Wert von 0 haben.

#include <limits.h>
MB_LEN_MAX
#include <stdlib.h>
MB_CUR_MAX
int mblen (const char *, size_t n);
int mbtowc (wchar_t *pwc, const char *s, size_t n);
int wctomb (char *s, wchar_t wchar);
size_t mbstowcs (wchar_t *pwcs, const char *s, size_t n);
size_t wcstombs (char *s, const wchar_t *pwcs, size_t n);

Die Funktionen zur Handhabung von asiatischen Zeichensätzen sind in der stdlib.h beschrieben. Da ihre Anwendung im deutschsprachigen Raum eher unüblich ist, soll die Diskussion kurz gehalten werden.

Eine Zeichenkonstante aus einem erweiterten Zeichensatz mit durch eine vorangestelltes "L" definiert.

Für erweiterte Zeichen soll eine Ersatzdarstellung bestehend aus mehreren Bytes definiert werden. Setzt man mit setlocale() einen Schauplatz, dann gibt das Makro MB_CUR_MAX die maximale Anzahl von Bytes in einer Ersatzdarstellung für diesen Zeichensatz an. Die überhaupt mögliche Länge wird durch das Makro MB_LEN_MAX aus limits.h beschrieben.

Die Ersatzdarstellung wird Multibyte-Darstellung genannt, die Codedarstellung wide char (erweitertes Zeichen).

Die Darstellung eines erweiterten Zeichens kann über den Code sowie einen Zustand erfolgen. Ähnlich einer Umschaltung zwischen Groß- und Kleinbuchstaben, die unterschiedliche Codes auf die gleichen Tasten legt, kann eine Umschaltung der Codeinterpretation erfolgen. Die Möglichkeiten Codes darzustellen, werden damit noch einmal erweitert.

Bild 9-12: Konstante des erweiterten Zeichensatzes

Die verschiedenen Konvertierungsfunktionen wandeln erweiterte Zeichen von einer MB-(Multibyte) Darstellung in eines WC-(wide char) Darstellung und umgekehrt oder geben über benutzte Längen Auskunft.

Bei der Wandlung in die Ersatzdarstellung wird keine Endekennung für C-Texte angefügt. Im Beispiel werden die drei Funktionen zur Behandlung von erweiterten Zeichen benutzt. Mit wctomb() wird eine Ersatzdarstellung aufgebaut, mit mblen() die erzeugte Länge ermittelt und schließlich mit mbtowc() wieder die Wandlung rückgängig gemacht.

Die Ergebnisse sind beim verwendeten Compiler (TurboC++ 3.0) sehr einfach, da dieser die erweiterten Zeichen nicht unterstützt.

Bild 9-13: Wandlung WC zu MB-Darstellung

Die Funktionen mbtowc() und wctomb() haben noch eine Sonderaufgabe. Gibt man als Zeiger auf den C-Puffer eine ungültige Adresse NULL an, zeigt der Rückgabewert mit nicht 0 oder gleich 0 an, ob die MB-Darstellung Zustände benutzt oder nicht. Das gleiche Verhalten gilt für beide Funktionen.

Der zweite Teil der Funktionen für erweiterte Zeichensätze behandelt Strings.

Für Strings gibt es jeweils eine Funktion, die entweder in eine MB-Ersatzdarstellung oder in die WC-Darstellung wandelt. Das folgende Beispiel soll wieder die prinzipielle Verwendung zeigen, auch wenn der Nutzen wegen der fehlenden Unterstützung recht gering ist.

Die Funktion wcstombs() wandelt einen String aus erweiterten Zeichen in einen String der MB-Ersatzdarstellung. Umgekehrt setzt mbstowcs() eine Ersatzdarstellung in einen WC-Text um.

Die Rückgaben sind in beiden Fällen entweder die Nettolänge ohne abschließende Endekennung oder eine negative eins als Datentyp (size_t), um anzuzeigen, daß ein Wandlungsfehler passiert ist.

Bild 9-14: Wandlung von Strings

Mit der Erweiterung auf asiatische Zeichensätze hat C sicher seinen Ruf als Esperanto der Datentechnik gefestigt. Vielleicht können wir in Zukunft die Dokumentationen zu Programmen unter einer graphischen Benutzeroberfläche nicht nur in Deutsch oder Englisch sondern auch in Katakana lesen.


Zum Inhalt