Search code examples
pythonunpack

perl unpack function to python


I am trying to translate a Perl script into Python.

I have some trouble translating this line:

my ($sync,$pid,$afccc,@bytes)=unpack('CnCC*',$pkt);

I tried this in Python

bytes=[]
sync,pid,afccc,bytes=struct.unpack_from('BHBB',pkt)

But the variable bytes is not a list after this line, then the execution fails.

FYI, pkt is a 188 chars long string.


Solution

  • There are two problems, only one of which was fixed in Python 3:

    1. struct.unpack doesn't support arbitrary repetition. (Python 3.4 introduced iter_unpack, but that doesn't really help here.) You need an exact count for each specifier. You can't write 'BHB*B'; instead, you need to compute the number of bytes expected after BHB. Fortunately, it's not that hard or ugly to do:

      header_fmt = struct.Struct('BHB')
      packet_fmt = struct.Struct(header_fmt.format +
                                 '{}B'.format(188 - header_fmt.size))
      # packet_fmt.format == 'BHB183B'
      

      (In general you might need to use len(pkt) instead of hardcoding 188.)

      'BHB*B', though would be nice.

    2. In Python 2, you have to extract the trailing bytes from the array explicitly:

      fields = packet_fmt.unpack(pkt)
      sync, pid, afccc = fields[3:]
      bytes = fields[3:]
      

      Alternatively, you can use unpack_from and splicing, since you don't unpack bytes so much as you extract them.

      sync, pid, afccc = header_fmt.unpack_from(pkt)
      bytes = pkt[header_fmt.size:]
      

      In Python 3, you can just use *name syntax with tuple unpacking.

      sync, pid, afccc, *bytes = packet_fmt.unpack(pkt)