Tom2: Bits und Bytes in Unicode

Hallo Leute

Ich kämpfe momentan mit XML-Files, XPath und Schemas und versuche nun alle möglichen Fehlerquellen auszuschalten. Dabei habe ich natürlich auch einen Blick hinter die Unicode-Kulissen geworfen, was mehr in Verwirrung als in Klarheit endete: Mit meinem eigentlich ziemlich komfortablen (wenn auch äusserlich hässlichen) Texteditor UltraEdit habe ich mir ein per ASP generiertes XML-Dokument angeschaut, und zwar im Hex-Modus. Als Zeichensatz beim Erstellen habe ich UTF-8 verwenden wollen, was bei Microsoft als Codepage=65001 bekannt ist.

Zu Beginn war ich gleich etwas irritiert: Die ersten 31 Bits (bei 1 beginnend ;-) waren mit Einsen gefüllt (Hex: FF FE), was mir im Textmodus jedoch nicht angezeigt wurde. Dies scheint mir ein bei Unicode-Daten üblicher Datei-Anfang zu sein. Nur wofür ist der genau gut? Erkennt ein Programm daran das Unicode-Transformation-Format?

Gelegentlich führte dies zu Problemen, wenn beim Speichern etwas schief ging und diese Zeichenfolge in ein bekanntes Zeichen umgewandelt wurde (ich glaube nicht, dass dies mit Absicht geschieht). Damit kann ich jedoch leben, da es eher selten und vor allem bei ASP-Skripten passiert.

Was mich aber richtig stutzig machte ist, das trotz UTF-8 pro Zeichen zwei Bytes verwendet werden. Sollte das nicht anders sein? Ich dachte immer, bei UTF-8 würden die ASCII-Zeichen als 8-Bit-Sequenzen gespeichert, so wie es in http://de.wikipedia.org/wiki/UTF-8 beschrieben ist. Nach jedem ASCII-Zeichen folgt ein leeres Byte (Hex: 00), welches meine Daten unnötig auf die doppelte Grösse aufbläht. Was läuft denn hier genau ab?

Ich hoffe, dass jemand ein paar Antworten zu meinen Fragen posten kann. Falls du jetzt das ganze Posting vergebens gelesen hast, dann könntest du mir natürlich auch verraten, welche XML-Tools dein Leben vereinfacht haben ;-).

FG und besten Dank

Tom2

  1. Moin,

    Zu Beginn war ich gleich etwas irritiert: Die ersten 31 Bits (bei 1 beginnend ;-) waren mit Einsen gefüllt (Hex: FF FE), was mir im Textmodus jedoch nicht angezeigt wurde. Dies scheint mir ein bei Unicode-Daten üblicher Datei-Anfang zu sein. Nur wofür ist der genau gut? Erkennt ein Programm daran das Unicode-Transformation-Format?

    Das ist die UTF-16-Kennung für die little-endian-Bytereihenfolge, nennt sich BOM (= Byte Order Mark).

    Was mich aber richtig stutzig machte ist, das trotz UTF-8 pro Zeichen zwei Bytes verwendet werden. Sollte das nicht anders sein?

    Siehe oben: laut BOM verwendest Du UTF-16, nicht UTF-8.

    Für genauere Ausführungen kann ich http://www.activevb.de/rubriken/kolumne/kol_20/unicode.html empfehlen.

    lg, Konrad -

    --
    Der Genitiv ist des Dativs Tod
    1. Hallo Konrad!

      Das ist die UTF-16-Kennung für die little-endian-Bytereihenfolge, nennt sich BOM (= Byte Order Mark).

      Little-Endian-Bytereihenfolge, das kommt mir bekannt vor. Das heisst doch, dass FF FE im Hex-Editor für FEFF in hexadezimaler Darstellung steht, oder?
      BOM, das muss ich mir merken.

      Was mich aber richtig stutzig machte ist, das trotz UTF-8 pro Zeichen zwei Bytes verwendet werden. Sollte das nicht anders sein?

      Siehe oben: laut BOM verwendest Du UTF-16, nicht UTF-8.

      Dacht' ich's doch! Aber bereits das ASP-Skript generiert UTF-16-Code und nicht, wie per CodePage 65001 verlangt, UTF-8-Code. Ich werde mal Google zu diesem Phänomen befragen - Und natürlich 'von Hand' konvertierte XML-Dokumente für meine XPath-Tests verwenden. Ich hoffe, ich kann nun auch Elementnamen und nicht nur Wildcards verwenden ;-). *ausprobier*... Nein, das funktioniert immer noch nicht.

      Kannst du mir vielleicht sagen, weshalb der XPath-Ausdruck '/*/*/*' problemlos funktioniert, während der genauere Ausdruck '/*/*/Command_Code' nichts zurück liefert? Auch '//Command_Code' liefert momentan keine Elemente zurück - Ich muss das XML-File wohl mal in seine Bestandteile zerlegen...

      Für genauere Ausführungen kann ich http://www.activevb.de/rubriken/kolumne/kol_20/unicode.html empfehlen.

      Wow, vielen Dank! Der Artikel scheint wirklich ein wenig mehr Informationen zu beinhalten, als ich bis jetzt zu sehen bekam.

      FG und besten Dank!

      Tom2

      1. Moin,

        Little-Endian-Bytereihenfolge, das kommt mir bekannt vor. Das heisst doch, dass FF FE im Hex-Editor für FEFF in hexadezimaler Darstellung steht, oder?

        Nein, nicht ganz -- die Bytereihenfolge der Bytes im Hexeditor entspricht der auf der Festplatte (bzw. im RAM, stark vereinfacht). Die Reihenfolge spielt nur bei der Interpretation eine Rolle: In UTF-16 z.B. werden die Bytes (zuerst einmal) paarweise ausgelesen und als Zahl von 0 bis 65535 interpretiert:

        Zahl = Byte1 + Byte2 * 256

        Nun gibt es zwei Konventionen, um zu bestimmen, welches Byte das erste und welches das zweite ist. Die little-endian-Konvention besagt, dass man die Bytereihenfolge im Prinzip umdreht, d.h. das zuerst im Speicher liegende Byte ist das höherwertige (d.h. in obiger Gleichung muss man 'Byte1' und 'Byte2' vertauschen).

        Uh, schlechte Erklärung für ein im Prinzip einfaches Problem. :-(

        Kannst du mir vielleicht sagen, weshalb der XPath-Ausdruck '/*/*/*' problemlos funktioniert, während der genauere Ausdruck '/*/*/Command_Code' nichts zurück liefert? Auch '//Command_Code' liefert momentan keine Elemente zurück - Ich muss das XML-File wohl mal in seine Bestandteile zerlegen...

        Hmm. Nein, keinen blassen Schimmer.

        lg, Konrad -

        --
        Der Genitiv ist des Dativs Tod
        1. Hi,

          Nun gibt es zwei Konventionen, um zu bestimmen, welches Byte das erste und welches das zweite ist. Die little-endian-Konvention besagt, dass man die Bytereihenfolge im Prinzip umdreht, d.h. das zuerst im Speicher liegende Byte ist das höherwertige (d.h. in obiger Gleichung muss man 'Byte1' und 'Byte2' vertauschen).

          Uh, schlechte Erklärung für ein im Prinzip einfaches Problem. :-(

          Na, wenn das so einfach wäre ... ;-)
          Aber bevor Du jetzt aufstehst: falsch liegst Du nicht.

          Das folgende ist etwas durch einige Einschränkungen etwas vereinfacht, sollte aber der Erklärung selber nicht schaden.

          Die Zahl 287454020 (Basis 10) soll in einem 32 Bit großem Speicher dargestellt werden. Dieser Speicher wird noch bei Bedarf in Bytes, 8 Bit großen Teilen unterteilt. In hexadezimaler Darstellung (Basis 16) sind das dann

          0x11223344 als "Little Endian"; eigentlich nur die x86er.
          0x44332211 als "Big Endian"; fast alle andern, vor allem aber auch im Netzverkehr gültig.
          0x22114433 hatte der PDP-11; den gibt es teilweise noch im Embededbereich, da das Dingen nicht kaputtzukriegen ist. Wird aber nicht mehr produziert, sind alles Altbestände.

          Und dann natürlich die verschiedenen 64-Bit Architekturen, Embededsysteme mit weniger Bits (aber meist auch 2er Potenzen, also 16 Bit (selten) und 8 bzw 4 und 2 Bit. 1 Bt-Systeme sind mir auf Anhieb keine bekannt udn die Syteme mit noch mehr Bits (gibt es Aktuelle?). Aber es müssen nicht unbdingte 2er Potenzen sein, und die Bytes nicht unbedingt 8 Bit groß (Byte != Oktet!) usw.

          Für den größten Teil der Menschheit sind aber nur Little Endian (meistverkauftes System ist nunmal einer aus der x86 Familie) und Big Endian (im Netz) wichtig.

          Aber das Leben hält auch so manche Überraschung bereit: vielleicht ist der nächste millionenschwere Auftrag ja auf einem noch abstruserem System auszuführen? ;-)

          Kannst du mir vielleicht sagen, weshalb der XPath-Ausdruck '/*/*/*' problemlos funktioniert, während der genauere Ausdruck '/*/*/Command_Code' nichts zurück liefert?

          Entweder gibt es den Pfad einfach nicht, "Command_Code" ist evt falsch geschrieben oder das Programm, das XPAth-Ausdrücke auswerten soll ist beschädigt.
          Ich persönlich würde erstes vermuten, ist zumindest der häufigste Fehler bei mir ;-)

          • Ich muss das XML-File wohl mal in seine Bestandteile zerlegen...

          Normalerweise sollte ein Programm, wor allem ein Parser, eine Schaltung "Geschwätzig" haben (meistens 'v' oder "verbose" o.ä., manchmal auch "debug"). Manche XML-Parser können bei entspr. Überredung auch den Baum ausgeben.

          Schau einfach mal in die Dokumentation, falls vorhanden.

          so short

          Christoph Zurnieden

  2. Moin!

    Was mich aber richtig stutzig machte ist, das trotz UTF-8 pro Zeichen zwei Bytes verwendet werden. Sollte das nicht anders sein? Ich dachte immer, bei UTF-8 würden die ASCII-Zeichen als 8-Bit-Sequenzen gespeichert, so wie es in http://de.wikipedia.org/wiki/UTF-8 beschrieben ist. Nach jedem ASCII-Zeichen folgt ein leeres Byte (Hex: 00), welches meine Daten unnötig auf die doppelte Grösse aufbläht. Was läuft denn hier genau ab?

    Dass du UTF-16 in deinem XML-Text verwendet hast, wurde schon gesagt. Die Frage bleibt: Aus welcher Quelle ist der Text in deinen Editor gelangt.

    ASP arbeitet intern in allen Strings mit Unicode, wobei unerheblich ist, ob die nun als UTF-16 oder UTF-32 codiert werden. Die Ausgabe an den Browser mit Response.Write() wird durch die Codepage-Angabe beeinflusst, Codepage 65001 steht für "UTF-8".

    Dateioperationen über das FileSystemObject, denen du per Parameter "unicode" befiehlst, speichern aber vollkommen unabhängig von der Codepage immer in UTF-16.

    Wenn du UTF-8 speichern willst, mußt du ein FileStream-Objekt verwenden, nur dem kannst du eine Codierung mitgeben.

    Speichern geht beispielsweise so:
    dim filestream
    set filestream=Server.CreateObject("ADODB.Stream")
    filestream.CharSet = "utf-8"
    filestream.Open
    filestream.WriteText "Ich bin Unicode-Text UTF-8..."
    filestream.SaveToFile filename,adSaveCreateOverwrite
    filestream.Close
    set filestream=Nothing

    Wiedereinlesen geschieht analog mit LoadFromFile und ReadText.

    Auch bei dieser Speicherform wird zu Beginn der Datei eine BOM geschrieben (drei Oktets).

    - Sven Rautenberg