1

I've got a bytestring "\x56\x20", which is two sets of data, a (12 bits) and b (4 bits).

The unpacked data is expected to be:

a = 86 b = 2

Where:

a = int("056", 16)
b = int("2", 16)

I know I can use binascii to convert the bytestring to a hex string and then work slice magic on it, but that seems messy.

I looked at struct but couldn't figure out a method to split out 12 bits/4 bits.

>>> import binascii
>>> two_octets = "\x56\x20"
>>> hex_str = binascii.hexlify(two_octets)
>>> temp_a, temp_b = hex_str[:2], hex_str[2:]
>>> a_part, b_part = reversed([c for c in temp_b])
>>> int(a_part + temp_a, 16)
86
>>> int(b_part, 16)
2
>>>

Is there a cleaner way?

2
  • 2
    If \x56\x20 is to be split in a 12 and a 4 bit section, you get 1378 and 0, not 86 and 2.. Unless this is little-endian, and thus should be interpreted as \x20\x56 really. Commented Oct 12, 2012 at 6:57
  • good point, I'm just looking at the raw bytes in a hex editor, so yes it appears it should be \x20\x56. Commented Oct 12, 2012 at 7:03

3 Answers 3

3

You appear to be interpreting the data as little-endian. To decode, decode with struct, then use bitshifting and a mask to interpret them:

import struct
two_octets = '\x56\x20'
values = struct.unpack('<H', two_octets)[0]
a = values & 0xFFF  # Select right-most 12 bits
b = values >> 12    # Select left-most 4 bits
Sign up to request clarification or add additional context in comments.

3 Comments

@monkut: It's fine. :-) I'm surprised at the downvote, that's all. As documented in the Python datamodel, python integers explicitly support bitmasking and shifting, because it's the accepted way of dealing with situations like these (also see wiki.python.org/moin/BitManipulation).
ok, say I have a definition for these fields, something like fields = (12, 4). given that definition is there a programmatic way to get the mask?
@monkut: Sure: mask = (2 ** fields[0]) - 1.
2

For binary analysis of non whole-byte data an external module like bitstring might help (it certainly will when things get more complicated than this):

>>> from bitstring import BitArray
>>> a = BitArray(bytes='\x20\x56')
>>> a.unpack('uint:4, uint:12')
[2, 86]

1 Comment

thanks! Looks nice, but I want to stick to the standard lib for this.
1
>>> import struct
>>> divmod(struct.unpack('<H', '\x56\x20')[0], 2 ** 12)
(2, 86)

7 Comments

If your about divmod function, it just return tuple (a / b, a % b).
Really, divmod? Too-clever-by-half, this would be thrown out in a codereview.
@MartijnPieters, please, can you explain what you mean?
Using divmod to extract the two separate values from the two bytes, is perhaps a semi-clever, but is far removed from the normal way of dealing with such bytes. It makes your code un-maintainable, I would expect a semi-experienced developer to understand bitshifting and masking, but when encountering this trick would require a lot more head-scratching than is strictly necessary. Thus, if I came across this in a code-review, I would reject it or replace it with a bitshift-and-mask operation instead.
@MartijnPieters, to be honest, I do not know to what problems this may cause. Also, in my case, the shift is given one time, and in your - twice, which can lead to potential problems. If I had a more difficult case to work with shifts, and would also be used bitshifting. But in this I see no reason to not use the simple division.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.