There has been a receipe already, but not supporting zero terminated 'C' strings. Zerotermination is marked with 'z', number before 'z', say 2z is not supported. You can use 2 'z' positions, however. Set tu=True outputs to [] instead of record.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | from struct import calcsize, pack, unpack
class ConcreteRecord(list):
def __init__(self, fields, data=None, tu = False):
self._fields = fields
#self.tup = ()
varspace = 0
if data == None: return
for f in fields:
name,fmt,start,stop = f
if fmt == 'z':
strlen = data.find('\0', (start+varspace))-(start+varspace)
val = data[(start+varspace):(start+varspace+strlen)]
varspace += strlen + 1
else:
val = unpack(fmt, data[(start+varspace):(stop+varspace)])
if len(val) == 1: val=val[0]
if tu == True:
self.append(val,)
else:
self.__dict__[name] = val
class RecordFactory:
def __init__(self,record_fmt):
self.fields = []
pos = 0
for field in record_fmt.split():
if field[0] == "#": continue
fmt,name = field.split('.')
if fmt == 'z':
size = 0
else:
size = calcsize(fmt)
self.fields += [(name,fmt,pos,pos+size)]
pos += size
def build(self, data, tu = False):
return ConcreteRecord(self.fields, data, tu)
#### EXAMPLE ##################################################################
if __name__ == '__main__':
myrf = RecordFactory("""
z.test
4B.ip
z.zeroterm
z.abc
>H.port
>I.session_id
""")
r = myrf.build("teast\x00\x00\x01\x02\x03" + "test\x00" + "abc\x00" + "\x00\x04" + "\xFE\xDC\xBA\x98")
print "test: ", r.test
print "ip: ", r.ip
print "zeroterm: ", r.zeroterm
print "second string: ", r.abc
print "port: ", r.port
print "session_id:", r.session_id
r = myrf.build("teast\x00\x00\x01\x02\x03" + "test\x00" + "abc\x00" + "\x00\x04" + "\xFE\xDC\xBA\x98",True)
print r
|