Chunkformat Decodieren Klasse entsprechend Type wählen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
dr_strom
User
Beiträge: 3
Registriert: Dienstag 30. Januar 2018, 08:35

Chunkformat Decodieren Klasse entsprechend Type wählen

Beitragvon dr_strom » Dienstag 30. Januar 2018, 09:33

Hallo zusammen,

ich bin in der Programmiersprache Python relativ neu, beherrsche aberbereits 1-2 andere (C und C++). Ich wollte mich für komplexere Aufgaben hier geistig etwas erweitern und habe daher angefangen Python zu lernen.

Hier habe ich jetzt eine kleine (aber für mich wichtige) "Übung".
Ich möchte ein Chunkformat decodieren und die darin enthaltenen Daten extrahieren und Informationen in einem Log festhalten.

Der Dateiaufbau ist ganz typisch:
1. 32-Bit Type (Integer LE)
2. 32-Bit Length (Integer LE)
3. Content (<length> Bytes)

Das ganze natürlich hierarisch, also es ist eine Art "List"-Chunktyp vorhanden. Ein Chunk könnte auf mal 4GiB groß sein. Auf parallele Auswertung muss ich vorerst nicht achten, das schaue ich mir später mal bei einem anderen Projekt an.

Ich habe mir das ganze wie folgt vorgestellt:
1. Eine Instanz der Klasse Reader liest aus der Datei 2x 4 Byte (Length und Type)
2. Type wird ausgewertet, für jeden möglichen wird gibt es eine Seperate Klasse die dessen Verarbeitung übernimmt (bekommt das Dateiobjekt übergeben um Daten selbst einlesen zu können) -> von der jeweiligen Klasse wird eine Instanz erstellt.

Alle Chunk-Typ-Klassen erben von einer Basisklasse Chunk.

Jetzt zu meiner Frage:
Aus C kenne ich das switch-case-Konstruk, welches es ja in Python3 nicht gibt. Ich könnte jetzt einfach ein sehr langes if-else bauen, welches alle möglichen Typen und die zugehörigen Klasseninstanzierungen enthält. Aber irgendwie finde ich das nicht schön. Etwa so (Code nicht ausformuliert, nur zur Anschauung):

Code: Alles auswählen

class Chunk:
   def __init__(self, f, length):
      #tue was für alle Chunks gleich ist
                               
#Typ 0 ist der Headchunk
class ChunkTyp0(Chunk):
   def __init__(self, f, length):
      Chunk.__init__(self, f, length)
      while length > 0:
         itype = int.from_bytes(f.read(4), byteorder='little')
                   ilength = int.from_bytes(f.read(4), byteorder='little')
                   length -= ilength + 8
                   if type == 0
                      obj = ChunkTyp0(f, length)
         elif type == 1:
            obj = ChunkTyp1(f, length)   
         elif type == 2:
            obj = ChunkTyp2(f, length)
         else:
            print('Unknown Chunktype')
            f.read(ilength)
      
class ChunkTyp1(Chunk):
   def __init__(self, f, length):
      Chunk.__init__(self, f, length)
      #Verarbeite den Chunk

class ChunkTyp2(Chunk):
   def __init__(self, f, length):
      Chunk.__init__(self, f, length)
      #Verarbeite den Chunk

class Reader:
   def __init__(self, f)
            while True:
                type = int.from_bytes(f.read(4), byteorder='little')
                length = int.from_bytes(f.read(4), byteorder='little')
                if length == '':
                    break
                if type == 0
                obj = ChunkTyp0(f, length)
      elif type == 1:
         obj = ChunkTyp1(f, length)
      elif type == 2:
         obj = ChunkTyp2(f, length)
      else:
         print('Unknown Chunktype')
         break
         
if __name__ == '__main__':
   with open(argv[1], "rb") as f:
      main_Reader = reader(f)


Wie würdet ihr dieses Problem lösen? Vielleicht kennt Ihr ja einen eleganteren Weg. Ich hatte an die Dictionarys gedacht, weiß aber derzeit nicht, wie das syntaktisch gehen könnte. Das die Typeauswahl doppelt ist stört mich irgendwie am meisten.
Viele Grüße und danke für euren Input
Sirius3
User
Beiträge: 7050
Registriert: Sonntag 21. Oktober 2012, 17:20

Re: Chunkformat Decodieren Klasse entsprechend Type wählen

Beitragvon Sirius3 » Dienstag 30. Januar 2018, 10:22

@dr_storm: Deine Idee mit Dictionaries ist richtig.

Zum Code: Deine Einrückungen sind kaputt. Eingerückt wird generell mit 4 Leerzeichen pro Ebene (keine Tabs und auch nicht 3 Leerzeichen). Statt int_frombytes würde ich Dir struct.unpack empfehlen. Der Reader ist eigentlich keine Klasse, sondern eine Funktion `read_chunk`, der einfach einen Chunk zurückliefert. Diese Funktion kannst Du sowohl im Hauptprogramm als auch in den Chunks.__init__-Methoden aufrufen.

  1. import sys
  2. import struct
  3.  
  4. CHUNK_TYPES = {}
  5. def read_chunks(f, length=None):
  6.     if length is None:
  7.         pos = f.tell()
  8.         f.seek(0, 2)
  9.         length = f.tell()
  10.         f.seek(pos, 0)
  11.     result = []
  12.     while length > 0:
  13.         type, chunk_length = struct.unpack('<II', f.read(8))
  14.         result.append(CHUNK_TYPES[type](f, chunk_length))
  15.         length -= chunk_length + 8
  16.     return result
  17.  
  18. class Chunk:
  19.     def __init__(self, f, length):
  20.         #tue was für alle Chunks gleich ist
  21.         self.chunks = read_chunks(f, length)
  22.                                
  23. #Typ 0 ist der Headchunk
  24. class ChunkTyp0(Chunk):
  25.     def __init__(self, f, length):
  26.         Chunk.__init__(self, f, length)
  27. CHUNK_TYPES[0] = ChunkTyp0
  28.  
  29. class ChunkTyp1(Chunk):
  30.     def __init__(self, f, length):
  31.         Chunk.__init__(self, f, length)
  32.         #Verarbeite den Chunk
  33. CHUNK_TYPES[1] = ChunkTyp1
  34.  
  35. class ChunkTyp2(Chunk):
  36.     def __init__(self, f, length):
  37.         Chunk.__init__(self, f, length)
  38.         #Verarbeite den Chunk
  39. CHUNK_TYPES[2] = ChunkTyp2
  40.  
  41. def main():
  42.     with open(sys.argv[1], "rb") as f:
  43.         chunks = read_chunks(f)
  44.  
  45. if __name__ == '__main__':
  46.     main()
dr_strom
User
Beiträge: 3
Registriert: Dienstag 30. Januar 2018, 08:35

Re: Chunkformat Decodieren Klasse entsprechend Type wählen

Beitragvon dr_strom » Dienstag 30. Januar 2018, 10:44

Hallo Sirius,
vielen Dank. Das sieht schon viel strukturierter aus. Der doppelte Code ist weg, viel eleganter.

Das mein Code nicht funktioniert wusste ich. Ich hab Ihn direkt ins Forum getippt um zu zeigen was ich meine (mit Tab, mein vim macht bei py dateien aus einem Tab 4 Leerzeichen; trotzdem danke für den Hinweis).

Als C-Programmierer sind die Zeilen 27,33 und 39 für mich natürlich außerst ungewohnt, aber damit werde ich früher oder später klar kommen.

Die Lösung mit dem length=None gefällt mir gut. Aber wenn ich deinen Code richtig verstehe, wird read_chunks nun bei allen Chunks ausgeführt (im Konstruktor der Basisklasse Chunk), also auch bei solchen die nur Rohdaten und keine weiteren Chunks enthalten. Oder?

Ich würde daher die Zeile 21 löschen und zwischen Zeile 26 und 27 wieder einfügen. Richtig?
Sirius3
User
Beiträge: 7050
Registriert: Sonntag 21. Oktober 2012, 17:20

Re: Chunkformat Decodieren Klasse entsprechend Type wählen

Beitragvon Sirius3 » Dienstag 30. Januar 2018, 10:54

@dr_strom: wie Dein Dateiformat aussieht, weißt nur Du.
dr_strom
User
Beiträge: 3
Registriert: Dienstag 30. Januar 2018, 08:35

Re: Chunkformat Decodieren Klasse entsprechend Type wählen

Beitragvon dr_strom » Dienstag 30. Januar 2018, 11:00

Wohl war, ich wollte nur sichergehen, dass ich deinen Code richtig verstanden habe.
Benutzeravatar
snafu
User
Beiträge: 5388
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Re: Chunkformat Decodieren Klasse entsprechend Type wählen

Beitragvon snafu » Dienstag 30. Januar 2018, 22:55

Eine kleine Anmerkung: Wenn man mit Indexwerten auf ein Dictionary zugreift, dann kann man genau so gut direkt eine Liste anstelle des Dicts nehmen. Sofern die verschiedenen Typen also tatsächlich durch aufeinanderfolgende Integer dargestellt werden, dann würde ich dieTyp-Klassen einfach hintereinander in eine Liste stecken.
shcol (Repo | Doc | PyPi)

Wer ist online?

Mitglieder in diesem Forum: brainstir