UTF-8 Strings mit Team Developer 3.1 erstellen

Neulich hatten wir die Aufgabe, mit Team Developer 3.1 einen Text in UTF-8 zu erstellen. Eine Recherche in der Windows API fördert zwei geeignete Funktionen zu Tage:

MultiByteToWideChar(..) und WideCharToMultiByte(..). Mit der ersten Funktion ist es möglich, irgendeine Codierung (ANSI, UTF-8 und eine Menge Codepages) in UTF-16 (WideChar) umzuwandeln. Mit der zweiten kann UTF-16 in irgendeine Codierung umwandeln. Der Weg ist also:
GuptaString — MultiByteToWideChar(..) –> UTF-16 — WideCharToMultiByte(..) –> UTF-8.

Die Bezeichnung der beiden Funktionen finde ich etwas verwirrend: Mit MultiByte ist eine Codierung aus einem großen Fundus gemeint – auch SingleByte-Codierungen wie ANSI. WideChar steht für die Windows-interne UTF-16LE-Codierung.

Stellt sich nur noch die Frage, wie man die Funktionen konkret in Gupta verwendet:

MultiByteToWideChar

mit den Parametern

_In_      UINT   CodePage = Codepage des umzuwandelnden String – in unserem Fall die Standard Windows ANSI Codepage – also die Konstante CP_ACP = 0x00
_In_      DWORD  dwFlags = Steuert Feinheiten in der Umwandlung einzelner Zeichen. Beispielsweise kann der Buchstabe „Ä“ in Unicode auf zwei Arten dargestellt werden. Einmal als einzelnes Zeichen Ä (U+00C4) mit dwFlags = MB_PRECOMPOSED (0x01)  oder als kombiniertes Zeichen aus A + ̈ (U+0041 und U+0308) mit dwFlags = MB_COMPOSITE (0x02)
_In_      LPCSTR lpMultiByteStr = der zu codierende String
_In_      int    cbMultiByte = Länge des zu codierenden Strings oder -1, wenn der zu codierende String mit 0 terminiert ist – was bei Gupta der Fall ist.
_Out_opt_ LPWSTR lpWideCharStr = Buffer, in den die Funktion den konvertierten UTF-16 String schreibt
_In_      int    cchWideChar = Länge des zur Verfügung gestellten Buffers in Zeichen(!) oder 0, wenn man nur die Länge des nötigen Buffers ermitteln möchte.

Zunächst muss man also diese Funktion aufrufen mit cchWideChar = 0 und erhält die nötige Länge des Buffers in Zeichen(!) zurück.
Aber was bedeutet das konkret? Wieviel Bytes sind das?
Die Antwort ist einfach – ein WideChar-Zeichen ist immer 2 Byte lang – also muss man den Rückgabewert mit 2 multiplizieren und kann per SalStrSetBufferLength(..) entsprechend Speicher bereitstellen.
Aber stimmt das wirklich? Denn Unicode umfasst mehr Zeichen als mit 2 Bytes codiert werden können. Ein Standardbeispiel ist der Violinenschlüssel musical_symbol_g_clef
(U+1D11E) mit der UTF-16-Codierung 0xD834 0xDD1E – also vier Bytes.
Die Antwort ist: Ja es stimmt wirklich. Ich habe das getestet. Liefert man den Violinenschlüssel in UTF-8 Codierung an (0xF0 0x9D 0x84 0x9E) gibt die Funktion MultiByteToWideChar(..) den Wert „2“ zurück – obwohl es sich in Wahrheit nur um ein Zeichen handelt.

WideCharToMultiByte

mit den Paramtern

_In_      UINT    CodePage = gewünschte Ziel-Codepage
_In_      DWORD   dwFlags = Flags die das Verhalten steuern, wenn ein umzuwandelndes Zeichen nicht in der Zielcodepage enthalten ist.
_In_      LPCWSTR lpWideCharStr = der zu codierende UTF-16 String
_In_      int     cchWideChar = Länge des zu codierenden Strings oder -1, wenn dieser terminiert ist
_Out_opt_ LPSTR   lpMultiByteStr = Buffer für den umgewandelten String
_In_      int     cbMultiByte = Länge des Buffers in Bytes
_In_opt_  LPCSTR  lpDefaultChar = Zeichen, das verwendet werden soll, wenn ein UTF-16-Zeichen nicht in der Zielcodepage enthalten ist
_Out_opt_ LPBOOL  lpUsedDefaultChar = Eigentlich eine Receive-Variable, die angibt, ob ein Default bei der Umwandlung verwendet werden muss. Für die Umwandlung nach UTF-8 muss die Variable aber mit dem Wert NULL übergeben werden. Das wiederum lässt Gupta leider nicht zu: Wenn man den Receive-Parameter für eine externe Funktion auf NULL setzt (z.B. so: Set bUsedDefaultChar = NUMBER_Null und damit die externe Funktion aufruft, bekommt man einen Laufzeitfehler). Die einzige Möglichkeit ist, in der Deklaration der externen Funktion diesen Parameter als BOOL zu definieren und den Wert 0 zu übergeben.)

StringToUtf8

Damit steht der Umwandlung nichts mehr im Weg:

Function: StringToUtf8  ! __exported
    Description: Converts a Gupta string to UTF-8
    Returns
        String:
    Parameters
        String: sAnsi
    Actions
        Return Utf16ToUtf8( StringToUtf16( sAnsi ) )

Function: StringToUtf16  ! __exported
    Description: Converts a Gupta string (ANSI) to UTF-16
    Returns
        String:
    Parameters
        String: sAnsi
    Local variables
        Number: nBufferUtf16Len
        String: sUtf16
    Actions
        If ( NOT sAnsi )
            Return ''
        Call SalStrSetBufferLength( sUtf16, 1 ) ! Buffer must be 1 or bigger - otherwise we get a Gupta error when calling MultiByteToWideChar(..)
        Set nBufferUtf16Len = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, sAnsi, -1, sUtf16, 0 )
        Call SalStrSetBufferLength( sUtf16, nBufferUtf16Len * 2 ) !  Buffersize in "WideChars" -  must be multiplied by 2 to get buffersize in bytes
        Call MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, sAnsi, -1, sUtf16, nBufferUtf16Len )  !hier wieder Buffer in Characters
        Return sUtf16

Function: Utf16ToUtf8  ! __exported
    Description: Converts a UTF-16-String to UTF-8
    Returns
        String:
    Parameters
        String: sUtf16
    Local variables
        Number: nBufferUtf8Len
        String: sUtf8
    Actions
        If ( NOT sUtf16 )
            Return ''
        Call SalStrSetBufferLength( sUtf8, 1 ) ! Buffer must be 1 or bigger - otherwise we get a Gupta error when calling WideCharToMultiByte(..)
        Set nBufferUtf8Len = WideCharToMultiByte( CP_ACP, 0, sUtf16, -1, sUtf8, 0, STRING_Null, 0 )
        Call SalStrSetBufferLength( sUtf8, nBufferUtf8Len )
        Call WideCharToMultiByte( CP_ACP, 0, sUtf16, -1, sUtf8, nBufferUtf8Len, STRING_Null, 0 )
        Return sUtf8

Quellcode gibt’s hier: UtfStringConverion.apt

Happy coding!

Advertisements

Über thomasuttendorfer
Ich bin Entwicklungsleiter bei der Softwarefirma [ frevel & fey ] in München. Wir entwickeln Business-Software für Verlage und verwenden dafür den Gupta Team-Developer sowie Visual Studio.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: