0

I have a Hex Array which looks like :

31 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

When converting this to binary this looks like : 0011000100110001

Each bit is a flag that relates to a number within the array. In this case this binary number would equal 2,3,7,10,11,15.

I'm not sure if there is a name for this notation, but is there any easy method to convert the hex about to get a list of decimal numbers as shown above.

So,

Each 0x31 equates to a byte or 8 bits.

Each 0x31 converts to 00110001.

The way that this binary is then supposed to be interpretted is.

0 1 2 3 4 5 6 7 8 9 10
0 0 1 1 0 0 0 1 ......

Here you can see I get the decimal values 2,3,7 from the 0x31.

Hope this makes sense. Any help would be greatly appreciated.

4
  • What is a "Hex Array"? Is it a list of strings? Is it a string that needs to be parsed? Commented Aug 8, 2012 at 20:13
  • 2
    How does the hex array look like 0011000100110001 in binary? I fail to see that conversion, nor how that binary string equals 2,3,7,10,11,15. Commented Aug 8, 2012 at 20:16
  • @felix001 In what blaxpirit's or my answer does not answer your question? Commented Aug 10, 2012 at 16:01
  • They all looked great answers however Im having quite a few issues running these on 2.4 due to the built in functions that aren't available like bin etc. Commented Aug 21, 2012 at 14:00

6 Answers 6

6

Transform everything in a big bit string and then enumerate it. Mostly similar to blaxpirit's answer but it does not use the [:2] hack.

array = [0x31, 0x31, 0, 0, 0]
[i for i, x in enumerate("".join(format(a, "08b") for a in array)) if x == '1']

Results in

[2, 3, 7, 10, 11, 15]
Sign up to request clarification or add additional context in comments.

2 Comments

I'm curious what you thought the [2:] was for. And I don't mean that in a mean, sarcastic way, I am genuinely curious.
Now it's very nice! I totally forgot about str.format. But just format is even more compact: format(0x31, '08b') == '00110001'.
5

So we have the hex numbers in a space-separated string.

s = '31 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00'

Now we split the string, convert each byte from hex string to int (int('31', 16) == 49), then convert it to binary string (bin(49) == '0b110001'), then take away the '0b' with [2:], add zeroes at the beginning so the sequence exactly 8 long ('110001'.zfill(8) == '00110001'). Then we join all the bit strings together in one string.

s = ''.join(bin(int(b, 16))[2:].zfill(8) for b in s.split())
# Now `s` is '0011000100110001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
  • An alternative to the above line, that works in Python 2.5:
    trans = {'0':'0000','1':'0001','2':'0010','3':'0011','4':'0100','5':'0101','6':'0110','7':'0111','8':'1000','9':'1001','a':'1010','b':'1011','c':'1100','d':'1101','e':'1110','f':'1111',' ':''}
    s = ''.join(trans[c] for c in s.lower())

Then we enumerate the bits, so every bit (b) will have a corresponding position (i), just as you described. We use a list comprehension and include only those positions at which the symbol is '1'.

r = [i for i, b in enumerate(s) if b=='1']
# Now `r` is [2, 3, 7, 10, 11, 15]

9 Comments

@felix001 Added some explanations.
+1, expected my version to be significantly faster than converting each byte to a binary string, but I did some timing and this is faster than my bit-shifting approach.
@F.J The code in the first revision could be even faster, but then I went for (what seemed to me) better clarity.
Just tried this out but unfortantly Im running Python 2.5.2. Is there a quick way around this as Im unable to upgrade.
@felix001 OK, I've added a replacement for the line that causes problems in legacy versions of Python...
|
2

Alright, so, first things first, you need to convert that hex array into integer, then to binary. This is pretty simple in python:

myBin = bin(int("".join(hexArray),16))[2:].zfill(len(hexArray)*8) #We slice to get rid of the "0b" prepended by the bin function. zfill puts in leading zeros so we don't miss anything

Following that, we can do some cool enumeration and list comprehension in order to get the numbers we need:

myInts = [off for x, off in enumerate(myBin) if x == "1"]

So, assuming you already have your hex in an array, this will give you the answer you are looking for.

1 Comment

zipping ain't so cool when you've got enumerate, which is perfect for cases like this.
1

Using a precomputed positions list:

arr = [0x31, 0x31, 0, 0, 0]
print [(8*byte_ind + i) for byte_ind, b in enumerate(arr) for i in positions[b]]
# -> [2, 3, 7, 10, 11, 15]

Where positions maps all (256) bytes to corresponding positions:

>>> def num2pos(n):
...     return [i for i, b in enumerate(format(n, '08b')) if b == '1']
... 
>>> positions = map(num2pos, range(0x100))
>>> positions[0x31]
[2, 3, 7]

If your array is actually a hex string then you could convert it to a bytearray:

>>> import binascii
>>> arr = bytearray(binascii.unhexlify(s.replace(' ', '')))
>>> arr
bytearray(b'11\x00\x00\x00\x00\x00...\x00')

Comments

0
>>> print int('31', 16)
49

From there, you can use a list comprehension or generator expression to do the multiple values.

Comments

0

I think the following does what you are looking for:

def to_int_list(hex_array):
    hex_str = ''.join(hex_array)
    value = int(hex_str, 16)
    i = 4*len(hex_str) - 1
    result = []
    while value:
        if value & 1:
            result.append(i)
        value = value >> 1
        i -= 1
    return result[::-1]


>>> to_int_list(['31', '31', '00', '00', '00', '00'])
[2, 3, 7, 10, 11, 15]

2 Comments

Thanks, but these didnt seem to work so well... >>> to_int_list(z) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in to_int_list ValueError: invalid literal for int() with base 16: '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 01 A2 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00'
@felix001 - It wasn't completely clear from your question whether your "array" was a list of strings or one long string. Try changing the first line to hex_str = hex_array.replace(' ', '') and it should work.

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.