issue with struct.unpack

Alexander Blinne news at blinne.net
Sun Aug 26 08:53:28 EDT 2012


On 26.08.2012 01:31, Dennis Lee Bieber wrote:
> The struct module relies upon the user knowing the format of the data.
> If your problem is that you have some null-terminated string data in a
> variable width field, you will have to locate the position of the null
> FIRST, and specify the appropriate "count" for the s format.

This gave me the idea of an Enhancement of the Struct class with an
additional format character (perhaps 'n') which corresponds to a
null-terminated string:

-----
# -*- coding: utf-8 -*-

import struct

class Nstr(object):
    def __init__(self, ncount):
        self.ncount = ncount

    def unpack(self, s):
        s = s.split('\0')
        return s[:self.ncount], '\0'.join(s[self.ncount:])

    def pack(self, *s):
        if len(s)!=self.ncount:
            raise ValueError
        for st in s:
            if '\0' in st:
                raise ValueError
        return '\0'.join(s)+'\0'

    def __repr__(self):
        return 'Nstr('+repr(self.ncount)+')'

class NStruct(object):
    def __init__(self, format):
        self.format = format
        if format[0] in '!=<>@':
            self.endianness = format[0]
            format = format[1:]
        else:
            self.endianness = ''
        self.chunks = []
        while len(format)>0:
            j = format.find('n')
            if j > -1:
                k = j-1
                while format[k].isdigit():
                    k-=1
                chunkformat, ncount, format = format[:k+1],\
                format[k+1:j], format[j+1:]
                ncount = 1 if len(ncount)==0 else int(ncount)
            else:
                chunkformat, ncount, format = format, '', ''
                ncount = 0
            stru = struct.Struct(self.endianness+chunkformat)
            l = len(stru.unpack("0"*stru.size))
            self.chunks.append((stru, l))
            if ncount > 0:
                self.chunks.append((Nstr(ncount), ncount))

    def unpack(self, data):
        res = []
        for sth, n in self.chunks:
            if isinstance(sth, struct.Struct):
                chunk, data = data[:sth.size], data[sth.size:]
                res.extend(sth.unpack(chunk))
            elif isinstance(sth, Nstr):
                chunk, data = sth.unpack(data)
                res.extend(chunk)
        return res

    def pack(self, *data):
        res = []
        for sth, n in self.chunks:
            chunk, data = data[:n], data[n:]
            res.append(sth.pack(*chunk))
        return ''.join(res)

    def __repr__(self):
        return 'NStruct('+repr(self.format)+')'

if __name__=="__main__":
    a = NStruct('h b 2n 2h')
    print repr(a)
    d = 'asdblah blah\0haha\0asdf'
    r = a.unpack(d)
    assert r == [29537, 100, 'blah blah', 'haha', 29537, 26212]
    print repr(d), repr(r)
    dd = a.pack(*r)
    print r, repr(dd)
    assert dd == d

-----

beware of bugs in the above code, i haven't testet it much yet.

Alex



More information about the Python-list mailing list