Python: Daten als signed oder unsigned interpretieren

OP (Firma: RUB) #4663099
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
Moderator #4663301
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.
OP (Firma: RUB) #4663315
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. :)
#4663341
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.
Moderator #4663371
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.
(Firma: Schweigstill IT) Persönliche Seite #4663425
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren