Umwandlung von Daten

Die Funktionen pack und unpack

Diese Seite zeigt Informationen zur Umwandlung von Daten zwischen verschiedenen Typen.
Jede Programmiersprache bietet dazu Funktionen, soweit es die Umwandlung 'innerhalb' von Programmen betrifft.
Die Funktionen dieser Seite zeigen jedoch Möglichkeiten zur Umwandlung von Daten 'außerhalb' von Programmen, d.h. beim Lesen und Schreiben externer Daten, z.B. von Dateien.
Die klassische Heimat dieser Funktionen ist Perl, mittlerweile verbreiten sie sich auch in andere Programmiersprachen.
Perl Perl Scripts für CGI und Systemverwaltung
pack Codierung von Variablen zu Binär-Strings
unpack Decodierung von Binär-Strings zu Variablen
templates Typen-Symbole für die Funktionen pack und unpack
mydump Funktion zum Experimentieren mit pack und unpack
Verwandte Themen Formatierung mit Funktion sprintf
Links Ausgewählte Links zum Thema 'Daten-Umwandlung'

Funktion pack

Diese Funktion dient zur Codierung - Umwandlung interner Daten (aus Variablen) in beliebige externe Datentypen - z.B. für die Ausgabe an Dateien.
Template ist ein String aus Typen-Symbolen (s.u.) und Zahlen. Damit werden die Regeln für die Ausgabe der Daten festgelegt.
Liste ist eine Liste von Variablen, welche in der angegebenen Reihenfolge so codiert werden, wie Template angibt.
Syntax:
pack (Template,Liste)
Beachten sie beim → Lesen und → Schreiben von Binär-Dateien auf Win-System die Festlegung von binmode - ansonsten werden unabsichtlich alle Bytes 0A in 0D0A umgewandelt.

String-Beispiele:

$t1 erzeugt "ABCD": Jede der Zahlen 65..68 wird mit Symbol c zu einem → ASCII-String codiert.
$t2 verwendet den Operator .. zur Erzeugung aller Zahlen von 97 bis 103. Das Symbol * bedeutet 'so oft wiederholen wie Elemente in der Liste vorhanden sind'.
$t3 ergibt wieder "ABCD": Symbol c (mal 4) codiert die Hex-Zahlen 0x41..0x44 zu einem → ASCII-String.
$t4 ergibt "abc\0": Symbol a codiert (nur) eine Variable und füllt bis zur angegebenen Länge (hier 4) mit Null-Bytes.
$t5 erzeugt den String "ab\0xy". a2 codiert 2 Zeichen, x fügt ein Null-Byte dazu.

$t1 = pack('cccc',65,66,67,68);
# $t1 = 'ABCD';
$t2 = pack('c*',97..103);
# $t2='abcdefg';
$t3 = pack('c4',0x41,0x42,0x43,0x44);
# $t3 = 'ABCD'
$t4 = pack('a4','abc','x');
# $t4 = "abc\0";
$t5 = pack('a2xa2','abc','xyz');
# $t5 = "ab\0xy";

Beispiele mit Ganzen Zahlen:

$t6 erzeugt die Bytes 01 00
$t7 erzeugt die Bytes 00 01
$t8 erzeugt die Bytes FF FF
$t9 erzeugt die Bytes 40 30 20 10
$t10 erzeugt wie $t8 die Bytes FF FF
Die erzeugten Strings kann man z.B. in Binär-Dateien speichern.

$t6 = pack('S',1);
$t7 = pack('S',256);
$t8 = pack('S',65535);
$t9 = pack('L',0x10203040);
$t10 = pack('s',-1);

Gleitkomma-Beispiele:

$ta erzeugt die Bytes AB AA AA 3F
Decodierung dieser Bytes zu $ra ergibt die Zahl 1.333333307 und zeigt damit die Grenzen der Single-Genauigkeit.
$tb erzeugt die Bytes 55 55 55 55 55 55 F5 3F
Decodierung zu $rb ergibt die Zahl 1.33333333333333
Die erzeugten Strings $ta,$tb können z.B. in Binär-Dateien gespeichert werden.
Details zum Gleitkomma-Standard IEEE-754.

$ta = pack('f',4/3);
$ra = unpack('f',$ta);

$tb = pack('d',4/3);
$rb = unpack('d',$tb);
Vorsicht !
Einige Optionen der Funktionen pack und unpack sind von der Umgebung (Betriebssystem) abhängig. Je nach System kann z.B. die Länge der Variablen oder die Reihenfolge der Bytes varieren.

Testen sie daher nach Möglichkeit ihre Programme bzw. die codierten / decodierten Daten auf allen erreichbaren Systemen !

Funktion unpack

Diese Funktion dient zur Decodierung - Umwandlung beliebiger externer Daten (z.B. aus Binär-Dateien) in interne Daten (Variablen).
Ältere Programme oder solche, die unbedingt Platz sparen müssen, speichern ihre Daten als Binär-Strings.
Mit unpack können sie die Daten solcher files decodieren. (Wenn sie selbst Daten speichern, dann verwenden sie dazu am besten das → XML-Format. )
Die Syntax entspricht jener von Funktion pack (s.o.)
Beispiel:
Die Zerlegung eines Strings (Zeichenkette, Text) in ein Array, dessen Elemente die → Zeichencodes der einzelnen Zeichen enthalten.
$s = 'Test';
@ac = unpack('c*',$s);
# @ac = (84,101,115,116);
Beispiel:
Zunächst wird in der for-Schleife ein Binärstring $t (Länge 256 byte) erzeugt, welcher sämtliche möglichen Werte enthält (0x00..0xFF).
Anm.: ein Binärstring ist ebenso wie ein 'gewöhnlicher' String eine Folge von Bytes, diese können jedoch jeden beliebigen Wert annehmen, nicht nur druckbare Zeichen.

Der Binärstring wird mit Funktion unpack in einen (lesbaren) String $x von Hex-Zeichen umgewandelt.. Man könnte diesen String $x bereits ausgeben, das wäre jedoch unübersichtlich.

Deshalb wird der String $x mit Funktion dump_format in eine halbwegs lesbare Form gebracht: Nach jedem Byte folgt ein Leerzeichen, nach je 16 Byte eine neue Zeile. Die Ausgabe sieht so aus:
00 01 02 03 ... 0D 0E 0F
...
F0 F1 F2 F3 ... FD FE FF
# Binärstring erzeugen
$t="";
for($i=0;$i<256;$i++) {$t.=chr($i);}
# Binärstring decodieren und anzeigen
$x=uc(unpack('H*',$t));
print dump_format($x)."\n";

sub dump_format {
my $s = shift;
my $i=0; my $j=0; my $t='';
for($i=0;$i<length($s);$i+=2) {
$t.=substr($s,$i,2);
if($j<15) {$t.=' '; $j++;}
else {$t.="\n"; $j=0;}
}
return ($t);
}
Beispiel:
Hier wird das Modell einer Binärdatei gelesen und decodiert. Sie enthält Strukturen fixer Länge (16 Byte für leichtes Debuggen), die jeweils aus einer ganzen Zahl (2 Byte), aus einem ASCII-String (10 Zeichen) und aus einer Gleitkomma-Zahl (4 Byte) bestehen.

Beachten sie beim Lesen und Schreiben von Binär-Dateien auf Win-System die Festlegung von binmode - ansonsten werden unabsichtlich alle Bytes 0A in 0D0A umgewandelt.

Funktion read liest je eine Struktur (16 Byte) von der Datei in Variable $rec .
Mit Funktion substr wird der jeweilige Teil-String isoliert.
Mit Funktion unpack werden die Daten je nach Type decodiert.
Strings werden hier mit einem Regulären Ausdruck von überschüssigen Leerzeichen (trailing spaces) befreit.

Die decodierten Daten können sofort ausgewertet oder (hier) für eine nachfolgende getrennte Auswertung (hier: in den Arrays @d_nr, @d_name, @d_z ) gespeichert werden.
Zuletzt werden die entnommenen und decodierten Daten in einer Schleife angezeigt. Sie steht stellvertretend für die nachfolgende Auswertung der Daten.
$ok = open(BINREAD,$io_path);
if(!$ok) {die 'Fehler';}
binmode BINREAD;
### Datei lesen
my (@d_nr,@d_name,@d_z);
$nrec=-1; $bread=1;
while($bytes_read) {
$bread=read(BINREAD,$rec,16);
if($bread==16) {
$nrec++;
# i*2
$dat=substr($rec,0,2);
$d_nr[$nrec]=unpack('S',$dat);
# string*10
$dat=substr($rec,2,10);
$dat=~s/\s*$//;
$d_name[$nrec]=$dat;
# f*4
$dat=substr($rec,12,4);
$d_z[$nrec]=unpack('f',$dat);
}
elsif($bread) {print "Fehler\n";}
}
close BINREAD;

### Auswertung
$i=@dat_nr;
print "Auswertung ($i records)\n";
for($i=0;$i<@dat_nr;$i++) {
print "($i)";
print ' i*2=' .$d_nr[$i];
print ' s*10='.$d_name[$i];
print ' f*4='.$d_z[$i]."\n";
}

Typen-Symbole (Templates) für pack & unpack (Auswahl)

SymbolBedeutungKommentar
AASCII-String Länge wie angegeben, wird mit Leerzeichen rechts aufgefüllt
aBinär-String wird mit Null-Bytes rechts aufgefüllt
BBinär-String fallende Bit-Reihenfolge, Länge in Bit wie angegeben, 1 Bit je Listen-Element
bBinär-Stringaufsteigende Bit-Reihenfolge
CZeichen (Byte)unsigned char
cZeichen (Byte)signed char
dGleitkomma double precision IEEE-754 (64 Bit, 8 Byte)
fGleitkomma single precision IEEE-754 (32 Bit, 4 Byte)
HHex-String fallende Reihenfolge, d.h. Hi-Nibble → Lo-Nibble
hHex-String steigende Reihenfolge, d.h. Lo-Nibble → Hi-Nibble
IGanze Zahl unsigned Integer, >=32 Bit je nach Betriebssystem
iGanze Zahlsigned integer
LGanze Zahlunsigned long (exakt 32 Bit, 4 Byte)
lGanze Zahlsigned long
NGanze Zahllong 'big-endian' (exakt 32 Bit, 4 Byte)
nGanze Zahlshort 'big-endian' (exakt 16 Bit, 2 Byte)
PZeigerpointer auf eine Struktur fixer Länge
pZeiger pointer auf einen Null-terminierten String variabler Länge
QGanze Zahl unsigned quad (exakt 64 Bit, 8 Byte) - abhängig von System und Perl-Version
qGanze Zahl signed quad (exakt 64 Bit, 8 Byte) - abhängig von System und Perl-Version
SGanze Zahlunsigned short (exakt 16 Bit, 2 Byte)
sGanze Zahlsigned short
U Unicode erzeugt UTF-8 code
uStringuuencoded string, uu-komprimiert
VGanze Zahllong 'little endian' (VAX, exakt 32 Bit, 4 Byte)
vGanze Zahlshort 'little endian' (VAX, exakt 16 Bit, 2 Byte)
wGanze ZahlSpezialfall 'BER'-komprimierte ganze Zahl
XBytebackup byte
xNullNull Byte
@Null Füllt mit Null-Bytes bis zur angegebenen Position
Eine nachgestellte Zahl kann je nach Symbol unterschiedliche Bedeutung haben:
Für Strings bezeichnet die Zahl die String-Länge. Jedes String-Symbol codiert / decodiert nur eine einzige Variable der Liste zu einem String der angegebenen Länge.
Für alle anderen Symbole bezeichnet die Zahl die Anzahl der Wiederholungen. Ein Template-String "d2S4" bewirkt die Umwandlung von 2 Double-Gleitkomma-Zahlen zu je 8 Byte und 4 Ganzen Zahlen zu je 2 Byte, d.h. codiert / decodiert einen Binär-String von insgesamt 24 Byte Länge.
Ein * als Multiplikator bedeutet: So oft wiederholen, als noch Elemente in der Liste sind.

mydump zur Analyse ge-'pack'-ter Daten

Dieses sub-Beispiel können sie dazu verwenden, um mit den Funktionen pack und unpack zu experimentieren.
Das sub erhält als Argument einen beliebigen 'binären String', wie er z.B. mit pack erzeugt wird.
mydump gibt einen hexadezimal codierten ASCII-String zurück. Anzahl und Anordnung der Bytes beschreiben den analysierten Binär-String. Die Bytes sind durch Leerzeichen getrennt.
Tipp: verwenden sie für eigene Funktionen niemals naheliegende & einfache Namen (wie z.B. 'dump'), die evtl. in einem der zahlreichen Perl-Module enthalten sein könnten.
sub mydump {
my ($s)=@_; my $d=''; my $i=0;
for($i=0;$i<length($s);$i++) {
if($i) {$d.=' ';}
$b=ord(substr($s,$i,1));
$d.=sprintf('%02X',$b);
}
return ($d);
}
Beispiel für die Anwendung:
Hier wird eine Zahl $x mit pack in eine single-Gleitkommazahl umgewandelt. Der erzeugte Binär-String $t hat eine Länge length($t) von 4 Byte und wird mit Hilfe der Funktionmydump angezeigt. Anschgließend wird der Binär-String mit unpack wieder decodiert und angezeigt.
Perdiodische Zahlen wie 1/3 oder 4/3 eignen sich gut zur Demonstration, da man mit einem Blick die Grenze der Genauigkeit erkennt.
$x=4/3;
$t=pack('f',$x);
print "x=$x   ";
print 'hex='.mydump($t);
$y=unpack('f',$t);
print "   y=$y\n";
Das gezeigte Beispiel ergibt diese Ausgabe:
x=1.33333333333333   hex=AB AA AA 3F   y=1.33333337306976
Das Ergebnis hat (single-Genauigkeit !) allerdings nur didaktische Bedeutung. - In der Praxis werden ausschließlich double-Gleitkomma Zahlen verwendet.

Ausgewählte Links zum Thema 'Daten-Umwandlung'

Perl: perlfunc (standardfunktionen) - pack & unpack
PHP: Offizielles Handbuch (de) - pack und unpack
Python: struct - Strings as packed binary data