ANSI und UNICODE

Während Windows 9x nur ANSI-kodierte Byte-Strings kannte, unterstützte NT 4 einige Unicode-Bereiche in der Form von UTF-16. Die Windows API ist daher auf NT-Systemen großteils doppelt implementiert.
Pessimistisch könnte man auch sagen: Sie ist gespalten.

Wer mit “alten” ANSI-Bytes-Strings arbeitet darf hinten ein großes “A” anhängen, und wer Unicode nutzen will, lässt seine APIs und Strukturen auf ein großes “W” für Wide-Character enden.

Die meisten kümmern sich jedoch darum nicht sondern nutzen die API ohne “A” oder “W” Suffix und verlassen sich darauf, dass die Windows-Header automatisch die richtige Form wählen.


Denn die bekannte API “MessageBox()” ist in Wahrheit nur ein Makro, das in etwas so aussieht:

1#if defined(UNICODE)
2#define MessageBox MessageBoxW
3#else
4#define MessageBox MessageBoxA
5#endif

Viele Programmierer machen den Fehler, dass sie aus Gründen der Einfachheit den Unicode-Support global abschalten (also das UNICODE Makro nicht definieren), damit sie leichter mit char basierten C-APIs reden können.

Doch damit schalten Sie in die Welt von Windows 95 und Windows 3.x zurück, womit z.B. Dateinamen mit Sonderzeichen aus fremden Sprachen zerstört werden.

ANSI

Der Begriff ANSI ist wieder einmal sehr unglücklich gewählt. Den er bedeutete ursprünglich “lokaler Zeichensatz” und steht heute für die Kodierung Windows-1252, also ASCII + die wichtigsten europäischen Sonderzeichen wie z.B. Umlaute.
Damit kann man in einem Byte alle lateinischen Buchstaben und zusätzliche noch “gestrichelte und bepunktete” Buchstaben darstellen.
Und da neben Englisch die Sprachen Deutsch, Spanisch und Französisch eine hohe Relevanz hatten, war diese Kodierung lange ausreichend, während auf z.B. griechische oder asiatische Zeichen verzichtet wurde. (OK, stimmt nicht ganz, für China und Japan gab es auch früher schon “Multi-Byte” Erweiterungen.)

Und früher bestand der “lokale Zeichensatz” (Codepage) aus einer Definition von bis zu 127 Zeichen, die in jedem Land etwas anderes bedeuteten. Der gleiche binäre Datensatz führte also in jedem Land zu einer anderen Darstellung bzw. recht lustigen Sonderzeichen.

UNICODE

Unicode ordnet jedem Zeichen aus jeder bekannten Schrift einen Zahlencode zu. Das geht sich natürlich nicht mit einem Byte (also 256 Werten) aus, weshalb Unicode-Zeichen heute bis zu 32-bit breit sind.

Doch als Windows NT Anfang der 90er entstand war der Entwurf noch 16-bit breit und somit sind Windows “Wide-Characters” ebenso 16-bit breit. Durch die UTF-16 Kodierung, kann man längere Zeichencodes aber auch auf mehrere 16-bit Blöcke verteilen.

Somit sollten in Windows Programmen immer die Wide-APIs genutzt werden, denn nur so ist garantiert, dass Programme in allen Ländern der Welt gleich funktionieren und nicht Zeichen verloren oder umgedeutet werden.

Byte-Order Marks (BOM)

Damit man nun bei einer Textdatei (oder auch bei Quellcodes) erkennen kann, welches Textformat vorliegt, wurde die Byte-Order-Mark erfunden. Es handelt sich dabei um ein paar Byte-Sequenzen die eindeutig festlegen, wie die nachfolgenden Bytes zu lesen sind.

Bei UTF-16 oder UTF-32 ist nämlich auch relevant ob die Blöcke im Little-Endian oder Big-Endian Format vorliegen. Und genau das lässt sich aus der BOM ablesen.

Linux

Linux hat sich hier entweder sehr smart oder ignorant verhalten und den Begriff Unicode anfangs erst gar nicht bedacht. Alle APIs nutzen Character-Strings, also reine Bytefolgen.
Zum Glück gibt es auch die UTF-8 Kodierung, wo alle Unicode-Zeichen auf 1 bis mehrere Bytes verteilt werden können und so meinte Linux einfach: Ich bin “automatisch” UTF-8 kompatibel.

GATE Implementierung

Strings sind im GATE Projekt bekanntlich auch reine Byte-Character Strings, die kein festes Kodierungsschema haben. Nur bei Konvertierungen wird von einer UTF-8 Kodierung ausgegangen.

Damit man nun aber solche historische Zeichen wie ANSI bzw. “Windows-1252” lesen kann, gibt es spezielle Konvertierungs-Routinen im gate/encode Modul. Dieses wandelt ANSI Bytes in UTF-8 kodierte Zeichen um und schreibt sie in einen StringBuilder.

Ähnliches ist auch mit UTF-16 und UTF-32 Texten möglich.

Selbstverständlich geht das ganze auch umgekehrt. Hier wird ein UTF-8 String in einen Stream mit der gewünschten Kodierung geschrieben.