Homepage     Computer-Stoff

Eine Einführung in Text-Codierungen für Binär-Dateien

Viele Programme können a priori nur mit Text umgehen, während ihre Benutzer auch Binär-Dateien mit ihnen verarbeiten wollen. Dies trifft beispielsweise für mail-Programme und Newsreader zu, die ihre Sachen über die textorientierten Protokolle SMTP und NNTP verschicken. Es trifft aber auch z.B. für Einträge in einem LDAP-Server zu.

Wenn man nun Binär-Dateien oder Texte mit Sonderzeichen mit solchen Programmen benutzen will, muß man sie als Text-Dateien codieren. Diese Datei soll einige der gebräuchlichsten Codierungs-Verfahren vorstellen.

C-Code gibt es hier nicht. Aber im Internet findet man zumindest für uuencode und base64 genügend Routinen.

Das Problem

7Bit / 8Bit

Einige sehr alte Programme, die mit ASCII-Texten umgehen sollen, verstehen auch nur ASCII-Zeichen, d.h. welche mit den ASCII-Werten zwischen 0 und 127. Bei solchen Programmen kann man nicht sicher sein, daß das achte Bit erhalten bleibt.

Textorientierte Protokolle

Die Protokolle SMTP oder NNTP arbeiten Text- und Zeilen-orientiert, d.h. sie lesen Zeile für Zeile ein und interpretieren gegebenenfalls den Text. Beispielsweise wird in beiden Protokollen eine Zeile, die nur aus einem Punkt besteht, als Ende der Nachricht interpretiert. Weiterhin muß jede Zeile mit einem Carriage-Return/Linefeed Paar enden. Weder sollte man sich darauf verlassen, was passiert, wenn z.B. nur das Carriage Return kommt, noch darauf, was passiert, wenn eine Zeile plötzlich mehrere MB groß ist.

Auch die dazugehörigen Clients sind in der Lage, Nachrichten zu verändern, sei es durch die Umwandlung von Zeichensätzen, das Einfügen von neuen Zeilenumbrüchen in lange Zeilen, das Ersetzen von Tabs durch Leerzeichen, oder das Entfernen von Leerzeichen am Ende von Zeilen.

Sonstige Steuerzeichen

Selbst wenn die obigen Probleme nicht zutreffen, so interpretieren viele Programme, die mit Text zu tun haben, bestimmte Zeichen. Beispielsweise könnte ein Null-Zeichen (d.h. das Zeichen mit dem ASCII-Wert 0) als Ende interpretiert werden.

Quoted printable

Die einfachste Möglichkeit ist natürlich, alle Zeichen, die Schwierigkeiten machen könnten, zu maskieren. Man kann dies z.B. tun, indem man ein Zeichen ersetzt durch ein "=" gefolgt von seinem ASCII-Wert in Hexadezimal-Schreibweise. Also beispielsweise

Ich w=FCnschte, da=DF es so einfach w=E4re.

Diese Art der Codierung nennt sich "quoted printable". Normalerweise werden alle nichtdruckbaren Zeichen außer Carriage Returns und Newlines, die Teil eines Carriage-Return-Newline-Paares sind, maskiert. Letztere sind die einzigen, die nicht maskiert werden dürfen. Für die Hexadezimalzahlen dürfen nur Großbuchstaben verwendet werden. Neue Zeilenumbrüche können hinzugefügt werden, indem eine Zeile mit einem "=" und einem newline abgeschlossen wird. Es gibt noch einige andere Regeln. Der genaue Standard wird festgelegt im RFC2045, in dem das MIME-Format beschrieben wird.

Natürlich könnte man jede beliebige Binär-Datei auf diese oder eine ähnliche Weise codieren. Das wäre jedoch viel zu unpraktisch, weil sie dabei fast dreimal so groß würde. Man benutzt quoted printable deshalb nur für Texte mit Sonderzeichen, beispielsweise mit Umlauten oder mit Buchstaben mit Akzenten.

24Bit-Codierungen

Welche Zeichen können auf jeden Fall übertragen werden? Nun, auf jeden Fall gehören Buchstaben a..z und A..Z und die 10 Ziffern dazu. Außerdem noch einige andere Zeichen wie "+", "=" und andere. Insgesamt kommt man auf mehr als 64, aber weniger als 128 Zeichen, die "sicher" sind. Die Idee ist nun also, daß man 24Bits aus 3Bytes der Ursprungsdatei umwandelt zunächst in 4 Zahlen à 6Bits, also zwischen 0 und 63, und dann in 4 Text-Zeichen.

Der erste Schritt sieht also wie folgt aus:

Ursprungsdatei (dezimal)      78       212         3
               (binär)     01001110  11010100  00000011
                           \____/\______/\______/\____/
6Bit-Teile     (binär)    010011  101101  010000  000011
               (dezimal)    19      45      16       3

Nun muß man nur noch diese Zahlen umkehrbar auf "sichere" Zeichen wie Buchstaben oder Ziffern abbilden. Hierzu gibt es zwei gebräuchliche Abbildungen: uuencode und base64.

uuencode

Hierbei wird einer Zahl n zwischen 0 und 63 das Zeichen mit dem ASCII-Wert n+32 (32 entspricht dem Leerzeichen) zugeordnet. Obiges Beispiel würde also zu

51 77 48 35, d.h. 3M0#

Da es speziell mit dem Leerzeichen aber Probleme gab, z.B. weil manche mail-gateways Leerzeichen am Ende einer Zeile abschneiden, codiert man das Null-Byte nicht durch 32 (" "), sondern durch 32+64=96 (den backtick "`"). Beim Decodieren schneidet man ja die obersten zwei Bits sowieso ab.

uuencoding steht für "Unix-to-Unix-encoding" und ist das älteste dieser Verfahren. Insbesondere im usenet ist es immer noch das Standard-Verfahren. Eine typische mit uuencode codierte Datei sieht wie folgt aus: (Hier als Beispiel der geschwungene Trennbalken aus dieser Datei.)

begin 644 loop.png
MB5!.1PT*&@H````-24A$4@```;H````D`0````#7E\*O````!&=!34$``+&/
M"_QA!0```#AT15AT4V]F='=A<F4`6%8@5F5R<VEO;B`S+C$P82`@4F5V.B`Q
M,B\R.2\Y-"`H4$Y'('!A=&-H(#$N,BG=%2Y)```!:4E$051XVMV5L6[$(`R&
M'66XD;5#I;Q"'Z`J+U8)I$KM:Z6ZX<8^0G/J"S`R('.V.7)`KM*5H5++@$CL
M#QO[)X'8-V;X3V!0G:#;=8++V`D>OSK!?2]X^'7P;=\+]K;C=>H$9RTR`-#?
M@PB-65QX(@,,V4MOP*4PKX,\/+TW`2:.JY)X*Q#)Q.9:XQP,6'9^C):6$8<6
M=*-$;33^0,ZIL/89%%)(/@Z!'Y>DC,SU-;((N1^>37Z(BQ+PD%U\<@C5*<-8
M'Y=VTIP9@:LP[+F@<WE*5S[(EEYQH0D\YH!Y;S^T02J09J<9=*9LM*PN(O.E
MWO`N-<V@8M#K<KM4!+.F7V:*C_+>3W''(*8JNJ*8-I-+52A\DA@$O$L?7Y*S
MJ:1W%NAT!8PJ.@'G3=E96@-KH='??0(_HS<,2I)SK30G0@/3R#8_!P&1\EJV
MVF99-L.N*P$I7P!URW=_W]S'<.4RW?;OP!]S?^K_>`(QTC#)CTX[)@````=T
:24U%!](,'@L:'$YMDUP`````245.1*Y"8((`
`
end

Die codierte Datei beginnt nach einer Kopfzeile, die mit begin anfängt. Auf das begin folgt der mode, d.h. zu Zugriffsrechte der zu erzeugenden Datei als Oktalzahl, und der Name der zu erzeugenden Datei.

Nach der Kopfzeile folgt zeilenweise die codierte Datei. Jede Zeile fängt mit einem Zeichen an, das angibt, wieviele Bytes in der Zeile codiert sind. Und zwar bekommt man die Anzahl der Zeichen, indem man 32 vom ASCII-Wert des Zeichens abzieht und vom Ergebnis die untersten 6 Bits nimmt. (Hier fängt jede Zeile mit einem "M" (ASCII-Wert 77) an, d.h. es sind 77-32=45 Zeichen codiert. Ergo folgen auf jedes "M" in der Zeile noch 45/3*4=60 Zeichen.) Die Zeilen sollten (samt dem schließenden Zeilenumbruch) nicht länger als 62 Zeichen sein.

Die Datei ist zu Ende, wenn eine Zeile kommt, in der kein Zeichen mehr codiert ist. Diese besteht meist nur aus einem "`" (backtick) (ASCII-Wert 96, was 0 codierten Zeichen in der Zeile entspricht). Ebenso könnte die Zeile auch aus nur einem Leerzeichen (ASCII-Wert 32) bestehen, was man aber wie gesagt vermeiden sollte.

Man beachte, daß in der vorletzten Zeile nur 26 Zeichen codiert sind, obwohl auf den ":" (Doppelpunkt) noch 36 Zeichen folgen und deshalb eigentlich 36*3/4=27 Zeichen codiert sein könnten. Das zusätzliche Byte muß beim decodieren weggeschmissen werden. Die Original-Datei bestand halt nicht aus einer durch 3 teilbaren Anzahl von Bytes.

Es folgt schließlich noch eine Zeile, die nur aus dem Wort "end" besteht.

Das genaue Format von uuencodierten Dateien ist allerdings nicht in irgendeinem Standard festgelegt und es gibt wohl ein paar Varianten, die mir aber nie untergekommen sind, und die sich im wesentlichen im genauen Format von Kopf- und Fußzeile oder in der Behandlung von Multi-Part-Postings unterscheiden. Manche fügen aber auch weitere Zeichen an das Zeilenende an, um die Integrität der Daten überprüfen zu können. Manche tolerieren den backtick am Ende nicht, sondern verlangen ein Leerzeichen.

base64

Das oben besprochene uuencoding benutzt immer noch einige Zeichen, die nicht kompatibel mit einem verbreiteten Zeichensatz sind, dem EBCDIC (Extended Binary Coded Decimal Interchange Code). So hat es ab und zu Probleme mit mail gateways gegeben, die zwischen ASCII und EBCDIC wechseln. Daher hat man sich noch eine zweite Möglichkeit ausgedacht, die Zahlen von 0 bis 63 auf Buchstaben und Ziffern abzubilden:

Zahl 0...2526...5152...616263
Zeichen A...Za...z0...9+/

Das Plus und der Schrägstrich sind dabei willkürlich gewählt worden. Die Codierung, die auf diese Weise entsteht, nennt man "base64". Sie ist die Standard-Codierung für mail-attachments und gilt als die sicherste Codierung. ("Sicher" bezieht sich hier auf die Datenintegrität. Bei den hier beschriebenen Methoden handelt sich nicht um Verschlüsselungsmethoden.) Typischerweise sieht ein solches mail-attachment dann so aus: (Wieder der geschwungene Balken als Beispiel.)

Content-Type: application/octet-stream; name=loop.png; SizeOnDisk=521
Content-Description: loop.png
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="loop.png"
MIME-Version: 1.0
Date: Mon, 30 Dec 2002 14:12:19 +0100 (CET)
Fcc: sent_mail
XFMstatus: 0000
From: My Name <my.address@somewhere.de>

iVBORw0KGgoAAAANSUhEUgAAAboAAAAkAQAAAADXl8KvAAAABGdBTUEAALGPC/xhBQAAADh0RVh0
U29mdHdhcmUAWFYgVmVyc2lvbiAzLjEwYSAgUmV2OiAxMi8yOS85NCAoUE5HIHBhdGNoIDEuMind
FS5JAAABaUlEQVR42t2VsW7EIAyGHWW4kbVDpbxCH6AqL1YJpErta6W64cY+QnPqCzAyIHO2OXJA
rtKVoVLLgEjsDxv7J4HYN2b4T2BQnaDbdYLL2AkevzrBfS94+HXwbd8L9rbjdeoEZy0yANDfgwiN
WVx4IgMM2UtvwKUwr4M8PL03ASaOq5J4KxDJxOZa4xwMWHZ+jJaWEYcWdKNEbTT+QM6psPYZFFJI
Pg6BH5ekjMz1NbIIuR+eTX6IixLwkF18cgjVKcNYH5d20pwZgasw7Lmgc3lKVz7Ill5xoQk85oB5
bz+0QSqQZqcZdKZstKwuIvOl3vAuNc2gYtDrcrtUBLOmX2aKj/LeT3HHIKYquqKYNpNLVSh8khgE
vEsfX5KzqaR3Fuh0BYwqOgHnTdlZWgNrodHffQI/ozcMSpJzrTQnQgPTyDY/BwGR8lq22mZZNsOu
KwEpXwB1y3d/39zHcOUy3fbvwB9zf+r/eAIx0jDJj047JgAAAAd0SU1FB9IMHgsaHE5tk1wAAAAA
SUVORK5CYII=

Dieses Format, das MIME-Format (Multipurpose Internet Mail Extensions) wurde dafür geschaffen, beliebig codierte Dateien in ein festes Schema zu bringen. Auch quoted printable oder uuencode ist möglich. Deshalb wird das empfangende mail-Programm hier durch "Content-Transfer-Encoding: base64" darüber informiert, daß die Datei mit base64 codiert wurde.

Bei base64-codierten Dateien können am Ende ein oder zwei Gleichheitszeichen vorkommen. Wenn die Originaldatei aus einer nicht durch drei teilbaren Anzahl von Bytes bestand, müssen

  1. für die Codierung 1 oder 2 zusätzliche Null-Bytes hinzugefügt werden, so daß die Gesamtzahl der Bytes ein Vielfaches von 3 wird.
  2. in der codierten Zeichenkette festgehalten werden, wieviele zusätzliche Bytes hinzugefügt wurden.

Wurde ein Null-Byte hinzugefügt, so ist in der codierten Zeichenkette das letzte Zeichen des letzten Quadruplets irrelevant und wird durch ein "=" ersetzt. Wurden zwei Null-Bytes hinzugefügt, so sind in der codierten Zeichenkette die letzten zwei Zeichen des letzten Quadruplets irrelevant und werden durch zwei "=" ersetzt. Die Anzahl der Gleichheitszeichen gibt also an, wieviele Null-Bytes der Original-Datei hinzugefügt werden mußten, um auf eine durch 3 teilbare Byte-Zahl zu kommen. Beim Decodieren müssen diese dann wieder abgetrennt werden.

Außerdem wurden an beliebigen Stellen hinter Code-Quadruplets Zeilenumbrüche eingefügt, so daß die Zeilen nicht länger als 76 Zeichen werden. Dies ist möglich, da bei der Decodierung alle Zeichen, die nicht zu den oben verwendeten gehören, ignoriert werden sollen.

Die base64 Codierung (ebenso wie ein Teil des MIME Formats) wird definiert im RFC2045. Ein anderes Format, welches base64-Codierung benutzt, ist das BinHex-Format, welches im RFC1741 definiert wird. Es ist vor allem auf Macintosh-Rechnern verbreitet. In der Praxis ist mir (als nicht-Mac-Benutzer) noch keine BinHex-Datei über den Weg gelaufen. Deshalb kann ich auch nicht mehr dazu sagen.

yEnc

Die oben vorgestellten 24Bit-Codierungen haben den Nachteil, daß sie eine Datei deutlich vergrößern, und zwar um einen Faktor 4/3 plus ca. 1.3% für neue Zeilenumbrüche. Aus 100KB werden also vielleicht 135KB. Das ist nicht wirklich nötig, denn zum einen gibt es heutzutage eigentlich keine Programme mehr, die das achte Bit einfach wegschmeißen. Zum anderen gibt es auch nur sehr wenige "kritische" Zeichen, die von den üblichen mail- oder news-Programmen als Steuerzeichen benötigt werden und/oder verändert werden können. Hierzu gehören insbesondere das Null-Zeichen, Newline, Carriage Return und Tab mit den ASCII-Werten 0, 10, 13, und 9. Nur diese müßten maskiert werden.

Ein neueres Codierungs-Verfahren namens yencode (yEnc) macht genau dies. Es funktioniert wie folgt:

  1. Lies ein Zeichen A aus der Originaldatei
  2. Berechne B = (A+42) modulo 256
  3. Wenn B keines der obigen kritischen Zeichen, und kein "=" ist, dann schreibe B in die Ausgabe-Datei
  4. Wenn B eines der obigen kritischen Zeichen oder ein "=" ist, dann schreibe ein "=" und (B+64) modulo 256 in die Ausgabe-Datei.
  5. Wenn die aktuelle Zeile der Ausgabe-Datei 128 oder mehr Zeichen enthält, dann mache einen Zeilenumbruch.

Nun, was soll das ganze? Wir müssen das Null-Zeichen maskieren. In Binär-Dateien kommen aber häufig große Blöcke von Null-Zeichen vor. Würden wir sie alle maskieren, wäre die entstandene Datei wesentlich größer als die Originaldatei. Deshalb lassen wir alle Zeichen zunächst einmal rotieren. (Schritt 2.) Das Null-Zeichen z.B. wird dadurch zu "*".

In Schritt 3. und 4. schreiben wir das so entstandene Zeichen in die Ausgabe-Datei, wobei wir die obigen kritischen Zeichen und das Gleichheitszeichen maskieren. Dieses brauchen wir nämlich, um eine Maskierung einzuleiten.

Da mail- oder news-Server zeilenweise arbeiten, fügen wir in regelmäßigen Abständen einen Zeilenumbruch ein, wobei das "=" und das maskierte Zeichen nicht getrennt werden dürfen. Die yencode-Spezifikation sieht eine Zeilenlänge von 128 vor, oder, falls das 128. Zeichen ein "=" ist, 129.

Genau wie bei uuencode werden die codierten Dateien üblicherweise mit einer Kopf- und einer Fußzeile versehen. Das Bild mit dem geschwungenen Balken sieht als yencodierte Datei ungefähr so aus:

=ybegin.line=128.size=521.name=loop.png.
.zxq74D4***7srn|**+.***N+****....***..kwk**..5&./***b.o..}.......*..J.......J]X[Z.JJ|..dJ[\Y\cYc^JRzxqJ.....J[X\S.?Xs**+.snk~....
....J6.G....m..lI.TY.3.t......hl..5Z\J..c.j....|..r.9E%Q..a."y.z.......3H.d..Y."....5..=M....W\*...2....L-6.u...Z..ff.a+P....U:..
...=MF6......;.@..n.^(j....C>|rh8.I....._.2.I.w...<.....2.S..I....C..Z.....t.h.....3f....i.kT...C.....XL....X_.......~.........y.
..J.T...`.u.R..B..uI......@../.Td+.w...-.....,i.a6t...^Ql-..`i1+.......`..U+S.*.........\....I...).,[.Z..xeP***1.swo1.6H5DFx...*.
***soxn.l...
=yend.size=521.crc32=EF707755.

Da dieser Abschnitt natürlich größtenteils nicht druckbare Zeichen enthält, ist es nicht möglich, die yencodierte Datei hier darzustellen. Ich habe alle nicht-druckbaren Zeichen durch einen Punkt ersetzt. In der Fußzeile gibt es außerdem noch eine Prüfsumme (crc).

yencodierte Dateien sind im Durchschnitt 1.6% größer als die Original-Dateien. Viele Binär-Dateien im usenet sind inzwischen nicht mehr uuencodiert, sondern yencodiert. Der Vorteil der geringeren Größe wird allerdings erreicht durch eine geringe Sicherheit für die Datenintegrität.

Das yenncoding wurde Jürgen Helbing eingeführt. Ende 2001 gab es deshalb lange Diskussionen, vor allem, weil yEnc nicht in den MIME-Standard integriert wurde, und weil man zum Erkennen der verschiedenen Teile einer Multi-Part-Binärdatei immer noch das "Subject" eines Artikels parsen muß. Da aber auf die Schnelle auch kein besseres Verfahren vorgeschlagen und implementiert wurde, hat sich yEnc nach dem Motto "it's good enough" durchgesetzt.

Weitere Informationen: www.yenc.org . Dort gibt es auch Links zu Programmen und die genaue Spezifikation.

Welche Programme machen das?

Normalerweise sollte man mit all diesen Codierungen überhaupt nichts zu tun haben, da die mail-Programme, Newsreader oder was auch immer sich darum kümmern sollten. Für yEncode sollte man sich eine neue Version seines Newsreaders besorgen. Ab und zu steht man aber manchmal doch vor dem Problem, eine Datei händisch en- oder decodieren zu müssen.

Auf Unix-Systemen gibt es hier für standardmäßig die Programme uuencode, uudecode, mimencode (auch zum Decodieren), und nicht ganz standardmäßig yencode und ydecode. Bei nicht ganz neuen Systemen muß man sich die beiden letzten von www.yencode.org besorgen.

Auf Windows-Systemen kenne ich mich nicht so gut aus. Es gibt ein DOS-Programm namens mpack, welches base64 und uuencodierte Dateien erzeugen und auspacken kann. Einen yencoder kann man sich über diverse Links bei www.yenc.org besorgen. Ob es Windows-Programme gibt, die eine für Windows-Benutzer gewohntere Oberfläche haben, weiß ich nicht.

Fehlt irgendetwas Wichtiges? Sagt mir Bescheid.


Homepage     Computer-Stoff by Michael Becker, 1/2003. Letzte Änderung: 1/2003.