Home > Net >  Swap hex digit pairs in bytes
Swap hex digit pairs in bytes

Time:03-30

I have a binary hex string, for example: b'\x914\x05\x11\x11\x95h\xf5' (the f is a filler in this case), and the expected result would be b'\x914\x05\x11\x11\x95h\xf5'91340511119568F519435011115986515.

To do this with the string and a loop is probably not the best solution (there are million on records), what would be a better way?

Edit: Forgot to mention the f is just a filler added from the server (network switch) to provide an even number for the switching (as mentioned by mkrieger)

CodePudding user response:

What you have is a bytes object. When iterating over it you get integer values:

>>> s = b'\x914\x05\x11\x11\x95h\xf5'
>>> list(s)  # equivalent to [b for b in s]
[145, 52, 5, 17, 17, 149, 104, 245]

To swap the two "nibbles" (the two 4-bit halves) of a byte, you can use bitwise operations, given that the byte is represented by its integer value. To select the higher-valued nibble, use a bit mask 0xF0 with bitwise and (&), and to select the lower-valued nibble, use 0x0F. Then shift them by 4 bits in the respective directions and combine them again with bitwise or (|):

def swap_nibbles(b: int) -> int:
    return ((b & 0xF0) >> 4) | ((b & 0x0F) << 4)

Now you can do this for all bytes in s to get a list of byte values with swapped nibbles:

>>> swapped_nibbles = [swap_nibbles(b) for b in s]
# [25, 67, 80, 17, 17, 89, 134, 95]

Now you just need to reassemble these numbers to your desired result. I'm not entirely sure what exactly you want, so here are some options:

  • To get another bytes object: Just use the bytes constructor.

    >>> bytes(swapped_nibbles)
    b'\x19CP\x11\x11Y\x86_'
    
  • To get an integer: Use the int.from_bytes constructor with big-endian byte order.

    >>> int.from_bytes(swapped_nibbles, byteorder='big')
    1820386708623558239  # == 0x194350111159865F
    
  • To get a hex string: Use hex on the above integer, or build it from the individual bytes:

    >>> ''.join(f'{b:02X}' for b in swapped_nibbles)
    '194350111159865F'
    

It's not clear to me what exactly the rules are for excluding the last F from the result, so you would have to add this logic yourself somehow.

  • Related