Forum: PC-Programmierung Python: Daten als signed oder unsigned interpretieren


von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Hallo Leute,

ich stehe gerade voll auf dem Schlauch :-/

Folgende Gesichte:
Ich moechte mit einem Python-Script Binaerdaten einlesen und 
entsprechend interpretieren.

Als Beispiel koennte man hier den Header einer BMP-Datei nehmen:
https://de.wikipedia.org/wiki/Windows_Bitmap

Dort gibt es Felder die als signed oder unsigned interpetiert werden 
sollten.

Und jetzt meine Frage: Wie mach ich denn sowas in Python?


Nehmen wir einfach mal als Beispiel das Datum: 0xFFFF

Jetzt kann ich sowas machen:
1
#!/usr/bin/env python
2
3
import struct
4
5
def main():
6
  x = struct.unpack('h', struct.pack('H', 0xFFFF))[0]
7
  y = struct.unpack('H', struct.pack('H', 0xFFFF))[0]
8
  print('x = {0} | y = {1}'.format(x, y))
9
10
11
if __name__ == '_main__':
12
  main()
Dann ist x = -1 und y = 65535. Soweit sogut.
Aber ich finde diese Loesung mega haesslich und umstaendlich.

Gibt es da was einfacheres/schoeneres?

Gruesse

von Mark B. (markbrandis)


Lesenswert?

Sollte das in Python nicht einfach zu machen sein, dann wäre es legitim 
die Frage zu stellen, ob man an der Stelle nicht eine andere 
Programmiersprache verwenden sollte.

Der Grundsatz ist immer: Wähle ein Werkzeug, das zur Aufgabenstellung 
passt.

von Bernd K. (prof7bit)


Lesenswert?

Kaj G. schrieb:

> x = struct.unpack(fmt, data)

> Aber ich finde diese Loesung mega haesslich und umstaendlich.

was ist daran hässlich? Eleganter und einfacher gehts doch kaum noch?

von Florian F. (flof3000)


Lesenswert?

Wenn man nicht alles in eine Zeile kneult und struct.unpack aliast wird 
das auch kurz und bündig...

Bspw
1
def read_whatever(bytes):
2
  su = struct.unpack
3
  x = su('h', bytes[:4])
4
  y = su('H', bytes[4:8])
5
  return x, y

fined ich jetzt nicht hässlich...

von Sven B. (scummos)


Lesenswert?

Florian F. schrieb:
> und struct.unpack aliast
Eeh, dadurch wird der Code definitiv viel lesbarer. Nicht.

Nein, eine bessere Lösung als mit struct gibt es m.W. nicht, aber wie 
schon gesagt wurde ist das doch auch ganz in Ordnung. Wie stellst du es 
dir denn vor?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Mark B. schrieb:
> Sollte das in Python nicht einfach zu machen sein, dann wäre es legitim
> die Frage zu stellen, ob man an der Stelle nicht eine andere
> Programmiersprache verwenden sollte.

Ich kenne keine Programmiersprache, in der das Extrahieren von Integer-
und Floating-Point-Zahlen, Zeichen und Strings bei gemischten Datentypen
auf portable Weise einfacher ginge als in Python.

Kaj G. schrieb:
> Als Beispiel koennte man hier den Header einer BMP-Datei nehmen:
> https://de.wikipedia.org/wiki/Windows_Bitmap

So geht's:

1
bfType, bfSize, bfOffBits, iSize, biWidth, biHeight,          \
2
  biPlanes, biBitCount, biCompression, biSizeImage,           \
3
  biXPelsPerMeter, biYPelsPerMeter, biClrUsed, biClrImportant \
4
  = struct.unpack('<2sI4xIIiiHHIIiiII', bmpHeader)

Hier werden alle Elemente des 54-Byte-Headers einzelnen Variablen
zugewisen, wobei bfReserved übersprungen wird, weil es nicht von
Interesse ist. Selbst in C tippt man dafür deutlich mehr Code.

Die Funktionen struct.pack und struct.unpack sind sozusagen das sprintf
und sscanf für Binärdaten.

: Bearbeitet durch Moderator
von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Okay, wie so oft: Das Problem sitzt vor dem Monitor :-/

Mein Problem bestand darin, dass ich die Daten zusammen geshiftet hatte:
1
f = open(dateiname, 'rb')
2
fc = f.read()
3
4
x = (fc[1] << 8) | fc[0]

Damit war es kein Byte-Object mehr, und deswegen geht das nicht direkt 
mit struct.unpack, sondern nur ueber den umweg mit 
struct.unpack(struct.pack(...)).

Lass ich dass mit der Shifterei funktioniert es direkt mit 
struct.unpack.
Damit gefaellt mir das auch schon viel besser :)
Danke euch.

Es gaebe wohl noch die Moeglichkeit mit ctypes:
1
import ctypes
2
3
f = open(dateiname, 'rb')
4
fc = f.read()
5
6
x = ctypes.c_int16((fc[1] << 8) | fc[0])
Aber die ctypes bringen wieder andere Probleme mit sich.

Tja, kaum hat man drueber gesprochen, loest sich dass Problem wie von 
selbst. :)


Yalu X. schrieb:
> So geht's:
...ist mir zu kryptisch :(
Ich mach es mir dann doch etwas aufwaendiger:
1
import copy
2
import struct
3
4
HEADER = {
5
        'bfType'            : None,
6
        'bfSize'            : None,
7
        'bfReserved'        : None,
8
        'bfOffBits'         : None,
9
        'biSize'            : None,
10
        'biWidth'           : None,
11
        'biHeight'          : None,
12
        'biPlanes'          : None,
13
        'biBitCount'        : None,
14
        'biCompression'     : None,
15
        'biSizeImage'       : None,
16
        'biXPelsPerMeter'   : None,
17
        'biYPelsPerMeter'   : None,
18
        'biClrUsed'         : None,
19
        'biClrImportand'    : None
20
    }
21
22
def get_header(bmp_file):
23
  header = copy.deepcopy(HEADER)
24
  
25
  header['bfType'         ] = struct.unpack('<H', bmp_file[ 0: 2])[0]
26
  header['bfSize'         ] = struct.unpack('<I', bmp_file[ 2: 6])[0]
27
  header['bfReserved'     ] = struct.unpack('<I', bmp_file[ 6:10])[0]
28
  header['bfOffBits'      ] = struct.unpack('<I', bmp_file[10:14])[0]
29
  header['biSize'         ] = struct.unpack('<I', bmp_file[14:18])[0]
30
  header['biWidth'        ] = struct.unpack('<i', bmp_file[18:22])[0]
31
  header['biHeight'       ] = struct.unpack('<i', bmp_file[22:26])[0]
32
  header['biPlanes'       ] = struct.unpack('<H', bmp_file[26:28])[0]
33
  header['biBitCount'     ] = struct.unpack('<H', bmp_file[28:30])[0]
34
  header['biCompression'  ] = struct.unpack('<I', bmp_file[30:34])[0]
35
  header['biSizeImage'    ] = struct.unpack('<I', bmp_file[34:38])[0]
36
  header['biXPelsPerMeter'] = struct.unpack('<i', bmp_file[38:42])[0]
37
  header['biYPelsPerMeter'] = struct.unpack('<i', bmp_file[42:46])[0]
38
  header['biClrUsed'      ] = struct.unpack('<I', bmp_file[46:50])[0]
39
  header['biClrImportand' ] = struct.unpack('<I', bmp_file[50:54])[0]
40
41
  return header

Aber das ist geschmackssache.
Was mich gestoert hatte, war die verschachtelung von 
struct.unpack(struct.pack(...)), die aber nur noetig war, weil ich die 
Daten von Hand zusammengeshiftet hatte.

Naja, Problem erkannt, Problem gebannt. :)

von Sven B. (scummos)


Lesenswert?

Ist halt unfassbar viel langsamer als wenn du es in einen einzelnen 
Aufruf tust aber jo, immerhin lesbar und funktioniert ;)

von Mark B. (markbrandis)


Lesenswert?

Yalu X. schrieb:
> Ich kenne keine Programmiersprache, in der das Extrahieren von Integer-
> und Floating-Point-Zahlen, Zeichen und Strings bei gemischten Datentypen
> auf portable Weise einfacher ginge als in Python.

Vielleicht habe ich den Threadersteller ja falsch verstanden. Ging es 
ausschließlich um das Extrahieren, oder darum wie man im weiteren 
Verlauf mit den Daten umgeht? Nach meinem Verständis behandelt Python 
bei arithmetischen Operationen eine Ganzzahl immer als 
vorzeichenbehaftet. Korrigier mich gerne wenn ich falsch liege.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Mark B. schrieb:
> Vielleicht habe ich den Threadersteller ja falsch verstanden. Ging es
> ausschließlich um das Extrahieren, oder darum wie man im weiteren
> Verlauf mit den Daten umgeht?

IMHO ersteres, aber genau weiß es nur Kaj ;-)

> Nach meinem Verständis behandelt Python bei arithmetischen Operationen
> eine Ganzzahl immer als vorzeichenbehaftet. Korrigier mich gerne wenn
> ich falsch liege

Richtig. Wobei das normalerweise kein Problem ist, da es in Python keine
Integer-Überläufe gibt.

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Besonders elegant ist bei Python natürlich auch die Behandlung der 
Endianess gelöst. Man gibt in der Formatangabe bei struct.pack bzw 
struct.unpack einfach einen Endianess-Qualifizierer an, siehe:

https://docs.python.org/3/library/struct.html#struct.calcsize

Die Endianess des Systems, auf dem ein Programm läuft, lässt sich auch 
direkt per sys.byteorder bestimmen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.