Die Community zu .NET und Classic VB.
Menü

Unicode

 von 

Einleitung 

In allen Zeiten der EDV stellte sich die Frage, wie Texte und einzelne Zeichen daraus in computerlesbarer Form abgespeichert werden können. Die gängigsten Verfahren vor dem Auftauchen von Unicode waren ASCII (7 Bit pro Zeichen) und ANSI (8 Bit pro Zeichen). Doch damit können nur sehr wenige Zeichen, genauer gesagt die der westeuropäischen Sprachen, kodiert werden. Bereits so wichtige Schriften wie Griechisch oder Kyrillisch waren kaum zu verwenden, ganz zu schweigen von Arabisch, Chinesisch usw. Die 256 Zeichen des ANSI-Zeichensatzes reichten bei weitem nicht aus. Aus diesem Grund wurde Unicode ins Leben gerufen.

Mit freundlichen Grüßen,
Philipp Stephani

Einleitung  

Diese Kolumne beinhaltet eine kurze Einführung in Unicode, um dann auf die verschiedenen Kodierungsmöglichkeiten und deren Verwendung in Visual Basic einzugehen. Ich glaube, dass dieser Artikel ziemlich nötig ist, da immer noch viele Personen nur geringes Wissen über Unicode besitzen; andere glauben weiterhin, dass Unicode "irgendetwas mit zwei Bytes pro Zeichen" sei und sind damit ebenfalls nicht mehr auf der Höhe der Zeit.

Anmerkung

Diese Kolumne bezieht sich nur auf Visual Basic 6.0, nicht auf Visual Basic.NET oder frühere Versionen, über die ich keine Aussage machen kann.

Da der Unicode-Standard ständig weiter entwickelt wird, kann dieser Artikel nur eine Momentaufnahme sein. Dieser Artikel basiert auf dem Unicode-Standard in der Version 4.0.

Der Unicode-Standard  

Unicode ist ein internationaler Standard, der vom Unicode-Konsortium entwickelt und von der International Standardization Organization (ISO) abgesegnet wird. Das Ziel des Standards ist, sämtliche Zeichen aller auf der Erde vorkommenden und vorgekommenen Schriften zusammenzufassen und jedem Zeichen eine eindeutige Nummer zu geben.

Das Unicode-Konsortium

Das Unicode-Konsortium ist eine gemeinnützige Organisation, die am 3. Januar 1991 gegründet wurde, um den Unicode-Standard weiter zu entwickeln. Mitglieder sind vor allem große Softwarefirmen wie Microsoft, Adobe, Apple, IBM, Sun oder HP. Seit der Gründung erscheinen regelmäßig neue Versionen des Unicode-Standards. Das Konsortium arbeitet eng mit der ISO zusammen, die Unicode unter der Bezeichnung "ISO 10646" normiert.

Zeichen  

Als Zeichen (character) wird jedes nicht weiter unterteilbare Unicode-Element bezeichnet. Dabei kann es sich um normale Buchstaben und Zahlen, aber auch um Steuerzeichen, Symbole oder asiatische Silben- und Wortzeichen handeln. Jedem Zeichen ist ein eindeutiger Zahlenwert (code point) zwischen 0 und &H10FFFF zugewiesen; es können also exakt 1.114.112 Zeichen verwendet werden. Dies ist ein riesiger Vorrat, der zur Zeit nur zu einem kleinen Teil gefüllt ist. Üblicherweise wird für die Darstellung von Unicode-Zeichen das Format U+zzzzzz verwendet, wobei "z" eine beliebige Hexadezimalziffer sein kann; die ersten beiden Ziffern sind optional und werden nur bei Zeichen größer U+FFFF angegeben, die letzten vier Ziffern sind für jedes Zeichen obligatorisch.

Der Unicode-Zeichenvorrat ist in 17 Ebenen (Planes) eingeteilt, von denen jede aus &H10000 (also 65.536) Zeichen besteht. Die Ebenen werden von 0 bis 16 durchnummeriert. Die erste Ebene (Plane 0) von U+0000 bis U+FFFF wird als Basic Multilingual Plane (BMP) bezeichnet und enthält fast alle heute verwendeten Zeichen. Die anderen Ebenen enthalten nur einige veraltete oder selten verwendete Zeichen und sind sogar noch größtenteils leer. Die Ebenen 15 und 16 (von U+F0000 bis U+10FFFF) sind komplett für den Privatgebrauch reserviert.

Nicht zum Unicode-Standard gehört die Darstellung von Zeichen und Texten. Die Kombination von Zeichen zu größeren Einheiten (z.B. Wörtern oder Silben) oder verschiedene Anzeigemöglichkeiten von Zeichen (beispielsweise im Arabischen) werden von Unicode nicht abgedeckt und den Implementationen bzw. Schriftarten überlassen.

Blöcke

Die einzelnen Ebenen sind noch weiter in Blöcke (blocks) unterteilt, von denen jeder die Zeichen einer Schriftfamilie enthält. Die Blöcke stellen lediglich eine Organisationshilfe dar, um Zeichen einer Schrift zusammenzuhalten; darüber hinaus haben sie keine Bedeutung. Einige wichtige Blöcke des Unicode-Zeichenvorrats heißen:

Blockname Erstes Zeichen Letztes Zeichen Zeichenanzahl Bemerkungen
Basic Latin U+0000 U+007F 128 Die Zeichen des ASCII-Zeichensatzes
Latin-1 Supplement U+0080 U+00FF 128 Die übrigen Zeichen des ANSI-Zeichensatzes
Greek and Coptic U+0370 U+03FF 144 Griechische und koptische Zeichen
Cyrillic U+0400 U+04FF 256 Kyrillisch
Latin Extended Additional U+1E00 U+1EFF 256 Viele zusätzliche Zeichen des lateinischen Alphabets
General Punctuation U+2000 U+206F 112 Interpunktionszeichen

Tabelle 1

Kodierung  

Es gibt verschiedene Verfahren, den abstrakten Wert eines Zeichens in eine konkrete Bytesequenz umzuwandeln; dieser Vorgang wird als Kodierung (encoding) bezeichnet. Der Unicode-Standard definiert drei Kodierungsformen (encoding forms), die mit "UTF-" (für "Unicode Transmission Format") beginnen. Die Zahl nach dem Bindestrich bezeichnet die Anzahl der Bit, die mindestens zur Kodierung eines einzelnen Zeichens verwendet werden:

Überblick

  • UTF-32: Am einleuchtendsten ist es natürlich, den Zeichenwert eins zu eins in eine entsprechend große Bytesequenz zu übertragen. Diese Sequenz müsste für ein Zeichen mindestens drei Byte breit sein, um die 17 Ebenen und die jeweils 65536 Zeichen darstellen zu können. Da aber wegen der technischen Konstruktion heutiger Computer nur Zweierpotenzen in Frage kommen, wählt man vier Bytes, was 32 Bit entspricht. Daraus besteht die UTF-32-Kodierungsform: sie ist eine einfache Übertragung des Zeichenwertes in eine 4-Byte-Zahl, z.B. dem Datentyp Long in Visual Basic. Diese Kodierungsform ist die Einfachste von allen, da alle Zeichen mit exakt 32 Bit dargestellt werden. Allerdings wird viel Speicher verschwendet, da das vierte Byte überhaupt nicht und das dritte Byte nur extrem selten ungleich Null ist.
  • UTF-16: Um dieser Verschwendung entgegen zu treten, wurde die Kodierungsform UTF-16 eingeführt. Diese kodiert jedes Zeichen mit einem oder zwei 16-Bit-Werten, wobei für alle Zeichen der BMP ein 16-Bit-Wert ausreicht. Für höhere Zeichen (U+10000 bis U+10FFFF) werden für die Kodierung zwei 16-Bit-Zahlen verwendet. Die Werte für diese Zahlen werden aus einem bestimmten, reservierten Bereich (U+D800 bis U+DFFF) genommen und als Surrogaten (surrogates) bezeichnet. Einzelheiten zur UTF-16-Kodierung siehe weiter unten.
  • UTF-8: In vielen westeuropäischen Texten stammen die große Mehrheit der Zeichen, darunter alle standardlateinischen Buchstaben, die Dezimalziffern und diverse Satzzeichen, aus dem originalen ASCII-Zeichensatz (U+0000 bis U+007F). Es liegt also nahe, für solche Texte eine Kodierung zu wählen, die die ASCII-Zeichen mit möglichst wenig Bytes darstellt. Diese Kodierungsform heißt UTF-8: Je nach Größe des Zeichenwertes besteht die Kodierung für das Zeichen aus einem bis vier Bytes. Dadurch werden UTF-8-Dateien, die hauptsächlich ASCII-Zeichen enthalten, bei gleicher Zeichenanzahl meist um einiges kleiner als UTF-16- oder gar UTF-32-Dateien.

Vor- und Nachteile

Folgende Aspekte müssen bei der Wahl der geeignetsten Kodierungsform beachtet werden:

  • Speicherplatz : Für europäische Texte wird bei UTF-16 und UTF-32 sehr viel Speicherplatz verschwendet. Zwar sind heutige Festplatten und Arbeitsspeicher groß genug, um auch UTF-32-Kodierungen zu erlauben, doch spielt die Dateigröße noch in vielen Situationen, z.B. beim Internetdownload, eine entscheidende Rolle.
  • Feste Zeichenbreite : Eine der häufigsten Zeichenfolgenoperationen ist die Bestimmung der Länge eines Textes. Wenn die Zeichen alle gleich breit sind, wie das bei UTF-32 der Fall ist, kann man die Länge einfach aus der Bytegröße des Speicherplatzes berechnen, z.B. durch eine Division. Umgekehrt gilt das Gleiche: Die benötigte Größe eines Zeichenfolgenpuffers im Arbeitsspeicher kann durch einfache Multiplikation berechnet werden. Bei den Kodierungsformen mit variabler Breite, UTF-16 und UTF-8, ist dies nicht möglich, sodass nicht so leicht festzustellen ist, wie viel Speicherplatz eine Zeichenfolge einer bestimmten Länge benötigt. Zwar kann ein Algorithmus zur Bestimmung des benötigten Speicherplatzes recht effizient implementiert werden, aber weil diese Berechung für fast jede Zeichenfolgenoperation durchgeführt werden muss, kann das Benutzen von UTF-32 trotzdem zu einer Geschwindigkeitssteigerung führen. Auch die Zugriffsgeschwindigkeit auf einzelne Zeichen kann ein Kriterium sein: Will man auf ein UTF-32-Zeichen zugreifen, so kann dessen Adresse im Speicher sehr leicht und schnell durch eine Multiplikation ermittelt werden, während die anderen Kodierungsformen eine Schleife benötigen, weil man die Breite der vorhergehenden Zeichen ja nicht kennt.
  • Geschwindigkeit : Bei UTF-32 entspricht der kodierte Zahlenwert direkt dem Unicode-Zeichen, bei UTF-16 und UTF-8 muss immer ein kleiner Algorithmus dazwischengeschaltet werden, der den Zeichenwert berechnet, was auch gewisse Zeit kostet. Allerdings relativiert sich dieses Argument dadurch, dass ein Unicode-Text ohnehin nicht wie früher Zeichen für Zeichen an die Grafikschnittstelle gesendet werden kann, da komplexere Zeichensätze (z.B. Arabisch oder Indisch) weitere Bearbeitung benötigen.

Jede Kodierungsform hat also Vor- und Nachteile.

Details

Dieser Abschnitt beschreibt kurz die Bitverteilung in UTF-16 und UTF-8. Da diese Kolumne kein technischer Artikel sein soll, wird auf eine exakte Beschreibung verzichtet. In dem beigefügten Beispiel werden Konversionsalgorithmen und ihre Verwendung demonstriert. Zur Übersicht werden in den folgenden Tabellen die Bitzahlen in Blöcke von je vier Bit aufgeteilt.

UTF-16

UTF-16 verwendet zur Kodierung von Zeichen außerhalb der BMP zwei Surrogaten. Der erste davon wird als hoher Surrogat (high surrogate) bezeichnet, der zweite als niedriger Surrogat (low surrogate). Der hohe Surrogat enthält die Ebenennummer und den oberen Teil des Zeichenwertes, der niedrige Surrogat nur den Rest des Zeichenwertes. Die Surrogaten sind für die UTF-16-Kodierung reserviert, deswegen ist die Kodierung eindeutig, d.h. kein Wert kann falsch interpretiert werden. Die folgende Tabelle gibt über die Bitverteilungen in den Surrogatenpaaren Auskunft:

Zeichenbereich Zeichenbits Kodierung bzw. hoher Surrogat Niedriger Surrogat
U+0000 bis U+FFFF (BMP) 0000-0000-aaaa-aaaa-aaaa-aaaa aaaa-aaaa-aaaa-aaaa  
U+10000 bis U+10FFFF 000c-cccc-aaaa-aabb-bbbb-bbbb 1101-10dd-ddaa-aaaa 1101-11bb-bbbb-bbbb

Tabelle 2

Dabei entspricht dddd dem Wert ccccc - 1.

UTF-8

Die Breite von UTF-8-Codes ist variabel und hängt von dem Wert des zu kodierenden Zeichens ab. Auch diese Kodierungsform ist eindeutig, da es für jedes Byte nur eine einzige Möglichkeit gibt.

Zeichenbereich Zeichenbits Erstes Byte Zweites Byte Drittes Byte Viertes Byte
U+0000 bis U+007F 0000-0000-0000-0000-0aaa-aaaa 0aaa-aaaa      
U+0080 bis U+07FF 0000-0000-0000-0bbb-bbaa-aaaa 110b-bbbb 10aa-aaaa    
U+0800 bis U+FFFF 0000-0000-cccc-bbbb-bbaa-aaaa 1110-cccc 10bb-bbbb 10aa-aaaa  
U+10000 bis U+10FFFF 000d-dddd-cccc-bbbb-bbaa-aaaa 1111-0ddd 10dd-cccc 10bb-bbbb 10aa-aaaa

Tabelle 3

Die Bytereihenfolge

Auf verschiedenen Plattformen werden Zahlen intern unterschiedlich dargestellt. So liegt bei Intel- und AMD-Prozessoren das niederwertigste Byte immer zuerst im Speicher ("little endian" = LE), bei Motorola-Prozessoren ist es genau anders herum ("big endian" = BE). Dieser Sachverhalt lässt sich am besten durch ein kleines Beispiel veranschaulichen:

Zahlenwert (hexadezimal) Bytes bei LE Bytes bei BE
12 12 12
1234 34-12 12-34
12345678 78-56-34-12 12-34-56-78

Tabelle 4

Unicode-Dateien, die in UTF-16 oder UTF-32 kodiert werden, können im "little endian"- oder im "big endian"-Format vorliegen. Um dies unterscheiden zu können, wird an den Anfang einer Unicode-Datei meist eine Bytereihenfolgemarke (byte-order mark = BOM) gesetzt, die die Bytereihenfolge angibt. Oft wird die BOM auch als allgemeine Signatur verwendet, um das ganze Kodierungsschema einer Datei zu ermitteln. Sie ist durch den Unicode-Standard einfach als die entsprechende Kodierung des selten verwendeten Zeichens U+FEFF definiert. Daraus ergeben sich die folgenden Versionen der BOM:


Abbildung 1: Tabelle 5

Die letzte Spalte der Tabelle gibt an, wie die BOM in einem ANSI-Editor aussähe. Da das Nullzeichen (U+0000) nicht dargestellt werden kann, wird es hier durch ein Fragezeichen (?) repräsentiert.

Die Kombination aus Kodierungsform und Bytereihenfolge wird als Kodierungsschema (encoding scheme) bezeichnet.

Um die Kodierung einer Textdatei festzustellen, kann folgender Algorithmus benutzt werden:

  1. Prüfe die ersten vier Bytes auf Übereinstimmung mit einer UTF-32-BOM;
  2. Prüfe die ersten drei Bytes auf Übereinstimmung mit der UTF-8-BOM;
  3. Prüfe die ersten zwei Bytes auf Übereinstimmung mit einer UTF-16-BOM;
  4. Wenn keine BOM entdeckt werden konnte, betrachte die Datei als ANSI-Datei.

Anzumerken ist, dass dieser Algorithmus nur für reine Textdateien verwendet werden sollte; wenn das Kodierungsschema von vorneherein bekannt ist oder irgendwie anders ermittelt werden kann (z.B. durch das encoding-Attribut in XML), sollte diese Angabe benutzt werden, da die Verwendung einer BOM weder verbindlich noch verpflichtend ist.

Unicode-Unterstützung  

Diese ganze Diskussion war bis jetzt eher theoretischer Natur. Doch um tatsächlich eine passendes Kodierungsform auswählen zu können, muss man wissen, inwieweit heutige Software Unicode überhaupt unterstützt.

Hierbei ist zunächst festzustellen, dass der Großteil der heute tatsächlich implementierten Kodierungen eine Untermenge von UTF-16 darstellen, nämlich nur die Zeichen der BMP (ohne Surrogaten). Dies entspricht zwar nicht dem Unicode-Standard, hat aber den Vorteil einer festen Zeichenbreite (zwei Bytes) bei akzeptabler Kompatibilität. Da sich nur wenige, unwichtige Zeichen außerhalb der BMP befinden, ist der Verlust verschmerzbar. Diese Art der Kodierung wird für den LPWSTR-Datentyp (" l ong p ointer to a w ide character str ing") von Windows und auch für den String-Datentyp von Visual Basic verwendet. Wenn Microsoft von "Unicode" redet (z.B. in der MSDN Library), ist fast immer diese Kodierungsart gemeint. Ab Version 2000 unterstützt Windows allerdings in vielen API-Funktionen (z.B. ExtTextOut) und Algorithmen (z.B. Sortierung) auch die Surrogaten.

Bei Windows muss zwischen der 9x-Linie (Windows 95, Windows 98, Windows 98 Second Edition und Windows Millennium Edition) und der NT-Line (Windows NT in allen Versionen, Windows 2000, Windows XP und alle zukünftigen Windows-Versionen) unterschieden werden. Während erstere nur bei wenigen API-Funktionen Unicode unterstützen, basieren letztere vollständig auf der oben beschriebenen Unicode-Kodierung. Da die Windows 9x-Linie nicht mehr weiter entwickelt wird, wird es in absehbarer Zeit nur noch Windows-Betriebssysteme mit Unicode-Unterstützung geben.

Als Visual Basic-Programmierer muss man sich bei jedem Programm entscheiden, ob man für API-Funktionen die jeweilige Unicode-Version, z.B. CreateFile W (W = "wide"), oder die ANSI-Version, z.B. CreateFile A (A = ANSI) verwendet. Die Entscheidung richtet sich danach, ob das Programm nur auf Windows NT-Systemen oder auch auf Windows 9x-Systemen laufen soll. Ist ersteres der Fall, kann man getrost überall die schnelleren Unicode-Funktionen benutzen. Will man aber die Kompatibilität zu Windows 9x-Systemen gewährleisten, muss man beachten, dass dort nur ausgewählte Funktionen tatsächlich in der Unicode-Version verfügbar sind. Dies erfährt man durch einen Blick in die MSDN Library, genauer in den "QuickInfo"-Abschnitt der Funktionsbeschreibung. In der Zeile "Unicode" können dort folgende Werte stehen:

  • "Implemented as Unicode and ANSI versions on Windows NT.": Die entsprechende Unicode-Funktion ist nur unter Windows NT verfügbar (z.B. CreateFile).
  • "Implemented as Unicode and ANSI versions on Windows and Windows NT.": Die entsprechende Unicode-Funktion ist auf allen Windows-Systemen verfügbar (z.B. MessageBox).
  • Keine Zeile, die mit "Unicode" beginnt: Es gibt nur eine Version der Funktion. Meistens handelt es sich dabei um Funktionen, die keine Zeichenfolgenargumente erwarten (z.B. CloseHandle).

Gibt es von einer Funktion Unicode- und ANSI-Versionen, so kann man diese durch Zusatz von W respektive A an den Funktionsnamen aufrufen bzw. deklarieren. Der Zugriff auf Unicode-Funktionen ist allerdings nicht so trivial, wie es scheint, und wird deshalb in einem besonderen Kapitel dargelegt.

Unicode mit Visual Basic  

Der Aufbau von Visual Basic-Zeichenfolgen

In Visual Basic bestehen String-Variablen aus einem vier Byte großen Deskriptor, der die Länge der Zeichenfolge in Bytes (nicht in Zeichen!) angibt, den eigentlichen Zeichenwerten und einem abschließenden Nullzeichen (U+0000). Dieses Nullzeichen wird aus Kompatibilität zu C und zur Windows-API hinzugefügt; denn dort wird die Länge einer Zeichenfolge an Hand des terminierenden Nullzeichens bestimmt. Diese drei Bestandteile liegen im Speicher direkt hintereinander:


Abbildung 2: Tabelle 6

Eine Visual Basic-Zeichenfolge besteht also aus 4 + (Zeichenanzahl * 2) + 2 Bytes, eine leere Zeichenfolge ("") folglich aus 6 Bytes. Die Len-Funktion liefert die Anzahl der tatsächlichen Zeichen (ohne das abschließende Nullzeichen), die LenB-Funktion die Anzahl der Bytes, die diese Zeichen belegen (wiederum ohne Länge und Nullzeichen).

Sehr wichtig im Zusammenhang mit Unicode-Zeichenfolgen ist auch die undokumentierte StrPtr-Funktion. Diese gibt die Adresse des ersten Zeichens in der Zeichenfolge zurück, in diesem Beispiel also die Adresse des "T".

Zeichenfolgenmanipulationen

Um Unicode-Zeichenfolgen in Visual Basic zu verwenden, benötigt man entsprechende Unicode-Funktionen. Diese sind mit den hauseigenen Funktionen wie Left$, Mid$, Right$, Trim$ und InStr schon mitgeliefert. Ein Problem stellt allerdings der Zugriff auf einzelne Zeichen dar. Zwar sollte dieser mit AscW und ChrW$ theoretisch problemlos möglich sein, allerdings liefert die AscW-Funktion einen Integer-Wert zurück, was bei Zeichen größer als U+7FFF Probleme verursacht. Außerdem ist ChrW$ zum Setzen von Zeichenwerten ziemlich langsam, da immer erst eine neue String-Variable erstellt werden muss. Folgender Code stellt einen adäquaten Ersatz dar:

Private Declare Sub GetChar Lib "kernel32.dll" Alias "RtlMoveMemory" _
    (ByRef Destination As Long, ByVal Source As Long, ByVal Length As Long)

Private Declare Sub PutChar Lib "kernel32.dll" Alias "RtlMoveMemory" _
    (ByVal Destination As Long, ByRef Source As Long, ByVal Length As Long)

Public Property Get LCh(ByRef Text As String) As Long
    Call GetChar(LCh, StrPtr(Text), 2)
End Property

Public Property Let LCh(ByRef Text As String, ByVal NewValue As Long)
    Call PutChar(StrPtr(Text), NewValue, 2)
End Property

Public Property Get RCh(ByRef Text As String) As Long
    Call GetChar(RCh, StrPtr(Text) + LenB(Text) - 2, 2)
End Property

Public Property Let RCh(ByRef Text As String, ByVal NewValue As Long)
    Call PutChar(StrPtr(Text) + LenB(Text) - 2, NewValue, 2)
End Property

Public Property Get MCh(ByRef Text As String, ByVal Position As Long) As Long
    Call GetChar(MCh, StrPtr(Text) + ((Position - 1) * 2), 2)
End Property

Public Property Let MCh(ByRef Text As String, ByVal Position As Long, ByVal NewValue As Long)
    Call PutChar(StrPtr(Text) + ((Position - 1) * 2), NewValue, 2)
End Property

Listing 1

Dieser Code verwendet die hinlänglich bekannte API-Prozedur RtlMoveMemory und die oben erwähnte StrPtr-Funktion, um schnell auf einzelne Zeichen zuzugreifen. Er ist in der IDE zwar langsamer als die VB-Funktionen, in der kompilierten Version aber durchnittlich doppelt so schnell. Alle Eigenschaften lesen oder setzen jeweils ein einzelnes 16-Bit-Zeichen der übergebenen Zeichenfolge:

  • LCh liest oder setzt das erste Zeichen. Es kann als schnellerer Ersatz für Left$(Text, 1) benutzt werden.
  • RCh liest oder setzt das letzte Zeichen. Es kann als schnellerer Ersatz für Right$(Text, 1) benutzt werden.
  • MCh liest oder setzt ein beliebiges Zeichen. Es kann als schnellerer Ersatz für Mid$(Text, Position, 1) benutzt werden.

Stellt man obigen Code in ein Standardmodul, so kann auf einfache Weise auf die Zeichen einer Zeichenfolge zugegriffen werden:

Const Überschrift As String = "Zeichenersetzung"
Dim Test As String

Test = "Test"
Call MsgBox(Test, vbInformation, Überschrift)               'Test

Call MsgBox(Hex$(LCh(Test)), vbInformation, Überschrift)    'U+0054 = T
LCh(Test) = &H52&                                           'U+0052 = R
Call MsgBox(Test, vbInformation, Überschrift)               'Rest

Call MsgBox(Hex$(MCh(Test, 2)), vbInformation, Überschrift) 'U+0065 = e
MCh(Test, 2) = &H6F&                                        'U+006F = o
Call MsgBox(Test, vbInformation, Überschrift)               'Rost

Call MsgBox(Hex$(RCh(Test)), vbInformation, Überschrift)    'U+0074 = t
RCh(Test) = &H73&                                           'U+0073 = s
Call MsgBox(Test, vbInformation, Überschrift)               'Ross

Listing 2

Der Zugriff ist also sehr einfach und intuitiv.

API-Funktionen

Wie API-Funktionen, die Unicode-Argumente verwenden, benutzt werden, wird hier am Beispiel der sehr einfachen MessageBox-Funktion erläutert. Wie oben erwähnt, ist diese Funktion sowohl in der Unicode- als auch in der ANSI-Version auf allen Windows-Systemen verfügbar. Wir deklarieren sie folgendermaßen:

Private Declare Function MessageBoxW Lib "user32.dll" _
    (ByVal hWnd As Long, ByVal lpText As Long, ByVal lpCaption As Long, ByVal uType As Long) As Long

Private Declare Function MessageBoxA Lib "user32.dll" _
    (ByVal hWnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal uType As Long) As Long

Private Const MB_ICONINFORMATION As Long = &H40&
Private Const MB_TASKMODAL As Long = &H2000&

Listing 3

Die beiden Deklarationen unterscheiden sich offensichtlich (neben dem Namen) nur im Datentyp der beiden Zeichenfolgenparameter. Dies liegt daran, dass Visual Basic beim Aufruf von API-Funktionen Zeichenfolgen, die als Argumente für Parameter, die mit ByVal und als String deklariert sind, verwendet werden, automatisch in ANSI-Zeichenfolgen konvertiert. Nach dem Aufruf wird die temporäre ANSI-Zeichenfolge dann wieder zurückkopiert und danach gelöscht. Um diesen Automatismus zu unterbinden, dürfen wir Unicode-Parameter nicht als String deklarieren. Da die API-Funktion in diesem Fall die Zeichenfolge genau so erwartet, wie sie ohnehin schon im Speicher aufgebaut ist, können wir uns auch das langsame Kopieren und Rückkopieren sparen; wir brauchen nur die Adresse des ersten Zeichens übergeben, die wir mit StrPtr ermitteln. Unsere beiden MessageBox-Versionen rufen wir also folgendermaßen auf:

Dim Text As String
Dim Überschrift As String

Text = "Hallo!" & vbNewLine & ChrW$(&H3B1&) & ChrW$(&H3B2&) & ChrW$(&H3B3&) 'einige griechische Zeichen
Überschrift = "Ein Beispiel"

Call MessageBoxW(0, StrPtr(Text), StrPtr(Überschrift), MB_ICONINFORMATION Or MB_TASKMODAL)
Call MessageBoxA(0, Text, Überschrift, MB_ICONINFORMATION Or MB_TASKMODAL)

Listing 4

Interessant ist, dass unter Windows 9x bei keiner der beiden Versionen die griechischen Zeichen angezeigt werden, unter Windows NT dagegen nur in der Unicode-Version.

In allen Betriebssystem gibt es die Funktionen MultiByteToWideChar und WideCharToMultiByte, die die Konversion zwischen verschiedenen Kodierungsschemen erlauben.

Beispielprojekt  

Das Beispielprojekt enthält den in diesem Artikel vorgestellten Code, die CEncodedFile-Klasse und ein Benchmarkprogramm zum Testen der oben vorgestellten Ersatzfunktionen.

Download des Beispielprojekts [6341 Bytes]

Zusammenfassung und Schlusswort (eigene Meinung)  

Wie wir gesehen haben, ist Unicode und dessen Verwendung in VB kein Hexenwerk. Es gibt also keinen Grund, Unicode nicht zu unterstützen.

Schon heute fährt der Zug eindeutig weg von ANSI und hin zu Unicode. Unicode sollte deshalb nicht als Alternative zu ANSI gesehen werden, sondern als neue Ära im Umgang mit Zeichen und Texten, die die vorherigen Standards langsam, aber sicher verdrängen wird. Leider schleppen viele Softwareprodukte noch ANSI-Altlasten mit, doch in nicht allzu ferner Zukunft wird sich Unicode mit großer Wahrscheinlichkeit vollständig durchgesetzt haben. Es gibt auch gar keine andere Möglichkeit: Die Sprachen und Bedürfnisse von außereuropäischen Kulturen können und dürfen nicht einfach ignoriert werden. Erst durch Unicode wird jede Schrift wirklich gleichberechtigt. Neuere, ernsthafte Programme sollten auf jeden Fall wenigstens die BMP und die drei Kodierungsformen unterstützen. Ich hoffe, dass diese Kolumne ein erster Schritt dazu sein kann.

Weiterführende Lektüre

Ihre Meinung  

Falls Sie Fragen zu diesem Artikel haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.

Archivierte Nutzerkommentare 

Klicken Sie diesen Text an, wenn Sie die 26 archivierten Kommentare ansehen möchten.
Diese stammen noch von der Zeit, als es noch keine direkte Forenunterstützung für Fragen und Kommentare zu einzelnen Artikeln gab.
Aus Gründen der Vollständigkeit können Sie sich die ausgeblendeten Kommentare zu diesem Artikel aber gerne weiterhin ansehen.

Kommentar von Andreas Leithäuser am 17.01.2011 um 15:27

Hat jemand eine Idee, wie man in GFA-BASIC die OCX en dazu bekommt, dass das Bildschirmhandling so wie bei Windows in Unicode abläuft, sprich das man z.B. in eine Textbox chinesischen und englischen Text genmischt eingeben kann? Kann ich Unicode in GFA-BASIC nutzen und wenn ja wie? Wenn Nein, muss ich dann mein komplettes Programm in VB schreiben oder sogar auf C, C++, etc. oder kann ich Mithilfe der API-Funktionen weiterhin GFA-BASIC und Unicode nutzen?

Für Anregungen und Tipps wäre ich sehr dankbar.

Gruß, Andreas Leithäuser

Kommentar von Andreas Leithäuser am 17.01.2011 um 15:05

Hat jemand eine Idee, wie man in GFA-BASIC die OCX en dazu bekommt, dass das Bildschirmhandling so wie bei Windows in Unicode abläuft, sprich das man z.B. in eine Textbox chinesischen und englischen Text genmischt eingeben kann? Kann ich Unicode in GFA-BASIC nutzen und wenn ja wie? Wenn Nein, muss ich dann mein komplettes Programm in VB schreiben oder sogar auf C, C++, etc. oder kann ich Mithilfe der API-Funktionen weiterhin GFA-BASIC und Unicode nutzen?

Für Anregungen und Tipps wäre ich sehr dankbar.

Gruß, Andreas Leithäuser

Kommentar von Claus am 23.08.2010 um 16:59

Hallo,
bitte stelle Deine Frage im Forum, dort wird sie schneller und besser beantwortet!

Kommentar von Nic am 23.08.2010 um 15:30

Hallo Herr Stephani,
eine prima Story, wie man Zeichen manipulieren kann, wenn sie denn im code gelandet sind.
Ich versuche vergeblich file Namen von der HD korrekt mit VBA einzulesen (via DIR Funktion). Beispiel: filename enthält ein ASCII 213 = ein "O mit Tilde drauf". Eingelesen wird jedoch ein echtes "O". Klar, dass ich dann nicht mehr auf das HD-file zugreifen kann, weil mir der korrekte Name fehlt. Wie kann ich korrekt mit VBA file Namen einlesen? Thanks, Nic

Kommentar von Philipp Stephani am 01.08.2010 um 16:09

@Chris Gast, Jürgen Maluche, Kurt Schnabel, Wilfried Flachmeier, H.Brunner: Fragen sind in den Foren besser aufgehoben und werden dort meist auch schnell beantwortet; die Notizen sind nicht sehr gut als Diskussionsplattform geeignet.

Kommentar von Chris Gast am 27.06.2010 um 09:30

Sehr geehrter Herr Stephani,
ich bin von Ihrem Artikel begeistert. Ich suchte nach der Möglichkeit, Unicodezeichen in einer MsgBox darzustellen, ohne mir etwas aufwendig basteln zu müssen.
Da fand ich nach 2 Tage Googlen zwei passende Artikel, wovon mir Ihrer sehr gut gefiel, weil Sie dabei auch (als einziger) die Funktion "StrPtr" erklären (= Stringpointer?).

Bei mir funktioniert die Messagebox" auch wie folgt:

Abfrage = MessageBoxW(0, StrPtr( MsgBoxAnzeige), _
StrPtr("Zeichenanalyse"), _
vbYesNoCancel + vbDefaultButton2)
' Bei "Ja" kommt "6" heraus, bei "Nein" kommt "7" heraus,
' bei Abbrechen eine "2".


Vielen Dank.

Eine andere Frage:
Ich verwende Word 2000. Wenn ein Schriftzeichen in der gewählten Schrift nicht vorhanden ist, wird ein recht unauffälliges Kästchen angezeigt. Kann man gezielt nach diesen Kästchen suchen, um sie zu beseitigen?
Bei Manuskripten mit vielen Seiten sind die Kästchen zu unauffällig.

Bei falschen Querverweisen ist es mir schon gelungen, diese rot zu färben und alle Seiten mit falschen Querverweisen in einer MsgBox aufzulisten. Aber bei den Kästchen bin ich noch hilflos.

mit freundlichen Grüßen
H.Chris Gast, Berlin

Kommentar von Jürgen Maluche am 21.07.2009 um 13:20

Ich suche einen VisualBasic-Programmierer, der ein bestehendes Programm unicodefähig umschreibt (Erfolgshonorar 1000 Euro). Mailadresse maluche@web.de

Kommentar von Kurt Schnabel am 27.01.2009 um 19:18

Werden durch veränderungen der Zeichen oder Zahlen auch die Schrift größen verändert??? Und ist es Möglich, diese Unicode - Zeichen auch nur im Alphabet mit Zahlen und Satzzeichen downzuloaden?

Gruß
K. Schnabel

Kommentar von TiKu am 24.08.2007 um 23:00

Wer ActiveX-Controls sucht, die Unicode darstellen können, findet auf www.timosoft-software.de einige.

Kommentar von Wilfried Flachmeier am 03.03.2007 um 10:47

Im Zusammenhang mit dem Adress-Austausch zwischen Thunderbird und Windows Address Book via LDIF-Dateien fiel mir auf, dass Mozilla und Microsoft offenbar unterschiedliche Auffassungen zu Unicode-Zeichen haben. Konkret: Microsoft setzt Nicht-ASCII-Zeichen bei Verwendung von StrConv und vbUniCode/vbFromUniCode nicht korrekt in eine nationale Codepage, bevor Base64-Passagen verarbeitet werden. - Gibt es eine veröffentlichte Lösung zur Konvertierung von Base64-Daten auf diesem Hintergrund ?
Danke für jeden Hinweis.

Kommentar von H.Brunner am 31.03.2006 um 11:11

Hallo!
Hat schon jemand von euch so eine Ersatzbibliothek, die Unicode kann, verwendet?

Ich denke da etwa an die UniToolbox:
http://www.unitoolbox.com/default.asp?page=utw_home_unitoolbox

Kommentar von Philipp Stephani am 22.08.2005 um 21:33

Zu der Forms-Bibliothek muss allerdings gesagt werden, dass diese zu Microsoft Office gehört und nicht weitergegeben werden darf.

Kommentar von E.T. am 22.08.2005 um 18:12

Es gibt natürlich in VB die Object Library Forms 2.0, die einige Controls für die Verwendung mit Unicode bereitstellt. Diese sind für die meisten Zwecke ausreichend, der darin enthaltene Rahmen ist jedoch eine Katastrophe, da man nichts auf ihn platzieren kann. Der bleibt immer im Vordergrund.
Außerdem gibt es gewerbliche Anbieter, die Controls verkaufen, verlangen aber dafür nicht unerhebliche Beträge.

Kommentar von Johannes Roth am 03.01.2005 um 22:21

Na Philipp, was ist denn mit dem Artikel über "Anzeige von Unicodezeichen in VB Programmen"!?
Will ja nich aufdringlich sein, aber wär schon ganz hilfreich...

Kommentar von Philipp Stephani am 09.05.2004 um 23:34

Ich hätte noch einen Verbesserungsvorschlag für die Zeichenauslesungs- und -setzungsfunktionen, womit sie noch einmal etwa doppelt so schnell gemacht werden können:

Option Explicit

Private Declare Sub GetMem2 Lib "msvbvm60.dll" (ByVal Address As Long, ByRef Value As Long)
Private Declare Sub PutMem2 Lib "msvbvm60.dll" (ByVal Address As Long, ByRef Value As Long)


Public Property Get LCh(ByRef Text As String) As Long
GetMem2 StrPtr(Text), LCh
End Property

Public Property Let LCh(ByRef Text As String, ByVal NewValue As Long)
PutMem2 StrPtr(Text), NewValue
End Property

Public Property Get RCh(ByRef Text As String) As Long
GetMem2 StrPtr(Text) + LenB(Text) - 2, RCh
End Property

Public Property Let RCh(ByRef Text As String, ByVal NewValue As Long)
PutMem2 StrPtr(Text) + LenB(Text) - 2, NewValue
End Property

Public Property Get MCh(ByRef Text As String, ByVal Position As Long) As Long
GetMem2 StrPtr(Text) + ((Position - 1) * 2), MCh
End Property

Public Property Let MCh(ByRef Text As String, ByVal Position As Long, ByVal NewValue As Long)
PutMem2 StrPtr(Text) + ((Position - 1) * 2), NewValue
End Property

Kommentar von Wolf am 16.04.2004 um 13:09

Vielen Dank an Philipp Stephani für den Artikel !

Wollte das Buch v. Kaplan kaufen :"Internationalization with VB" => gibt's aber nicht mehr ... :-( ;nur beim Autor für 250,- Steine!

Wenn man Programme schreibt, die dann plötzlich auch im asiatischen Raum laufen sollen, bekommt man einige Probleme ! Habe diese Probs bis jetzt(!) nicht zufriedenstellend lösen können.
Einige kleinere Hinweise kann man schon in Koflers Buch "Visual Basic 6" finden, aber das wars dann auch schon ...


Gruß
Wolf



Kommentar von Philipp Stephani am 15.03.2004 um 20:28

Mir ist keiner bekannt. Aber warum sollte Unicode irgendwann einmal ersetzt werden? So wie ich das sehe, kann es allen vorhandenen und jemals existend gewesenen Zeichen Positionen zuordnen.

Kommentar von Dominik Wagner am 15.03.2004 um 11:56

Welcher Code soll irgendwann den Unicode ersetzen?
Schonmal im voraus vielen Dank.

Kommentar von Philipp Stephani am 30.01.2004 um 17:33

Gute Idee! Wenn niemand was dagegen hat, würde ich mich auch da mal dranmachen.
Philipp

Kommentar von Johannes Roth am 30.01.2004 um 17:25

Sehr interessant!

Wie wäre es eigentlich mit einem weiteren Artikel, mit dem Thema: "Anzeige von Unicodezeichen in VB Programmen"
Würde sicher viele interessieren und ist die logische Konsequenz aus dem Artikel hier!

Bitte!

Kommentar von Philipp Stephani am 27.01.2004 um 18:50

OK, der letzte war wieder mal ich. Ich vergesse immer Namen und E-Mail-Adresse! *grr*

Kommentar von am 26.01.2004 um 19:26

Finde ich nicht. Gerade bei größeren Projekten ist es im Verhältnis zum Gesamtcode kein so großer Aufwand. Bei kleinen Projekten, die ich nicht veröffentliche, nehme ich aber auch keine Unicode-Unterstützung.

Kommentar von Claus von der Burchard am 26.01.2004 um 12:41

Der Einbau von Unicode ist meiner Meinung nach in den meisten Projekten nicht sinnvoll.

Viele der Projekte, die geschrieben werden, sind nicht global interessant und insofern reicht der ASCII-Einsatz einfach. Auch wenn ich die Grundidee sinnvoll finde, werde ich Unicode nur als Ausnahme benutzen, wenn es nötig ist, solange es keine sinnvolle Einbindung in die Ausgabe gibt. Der Aufwand ist einfach nicht rentabel.

Mfg
Claus

Kommentar von Philipp Stephani am 25.01.2004 um 19:51

Und es geht doch! :-)

http://de.geocities.com/st_philipp/Complex/Screenshots/Unicode.gif

Kommentar von Philipp Stephani am 25.01.2004 um 19:03

grundsätzlich stimmt das schon. Aber es gibt auch Möglichkeiten, Unicode darzustellen. Aber darum geht es doch hier eigentlich gar nicht. Es ging vor allem um die Kodierung und eine allgemeine Einführung. Wenn ich jetzt auch noch lang und breit die Anzeige ausdiskutiert hätte, wäre das Teil noch mal doppelt so lang geworden. Außerdem hab ich geschrieben, dass das meine Meinung ist. Ich finde, wenn VB schon intern nur Unicode verwendet, ist es eher eine Nachlässigkeit von MS, dies nicht noch auf die Controls zu übertragen. Naja, eventuell hätte man den Satz noch etwas präzisieren können. Aber wie gesagt: Um die Anzeige geht es mir gar nicht.

Kommentar von Konrad L. M. Rudolph am 25.01.2004 um 15:42

Zitat:
"Wie wir gesehen haben, ist Unicode und dessen Verwendung in VB kein Hexenwerk. Es gibt also keinen Grund, Unicode nicht zu unterstützen."

Schön, Philipp, aber da stimme ich nicht zu: VB unterstützt einfach kein Unicode, nämlich bei der Ausgabe; man kann sich in gewissem Rahmen mit API behelfen, aber es gibt keinen linearen Weg, Unicode-Strings in einer MsgBox, auf Buttons oder in einer Richtext-Box auszugeben.
Und damit kann man Unicode eben _nicht_ einfach mal so in VB verwenden. Und das ist ein gewichtiger Grund, Unicode eben doch noch nicht zu unterstützen; oder programmierst du etwa für all deine Anwendungen mal schnell alle Standardcontrols von VB nach?
Das hat dann aber mit RAD nix mehr zu tun.