There is an array in which each element is a two-byte word. How to swap the first and second bytes of each element?

  • one
    Can you give an example of the input and expected array (3-5 elements each)? Is Endianness before / during conversion? - MaxU
  • one
    In which python type are the elements stored? - Arnial

4 answers 4

Having an array array in which each element is stored in at least two bytes, to change the order of bytes, it suffices to call the array.byteswap() method :

 >>> import array >>> a = array.array('h', [1, 32767]) >>> a.tobytes() b'\x01\x00\xff\x7f' >>> a.byteswap() >>> a.tobytes() b'\x00\x01\x7f\xff' >>> a array('h', [256, -129]) 

It can be seen that the numbers were stored in an array using native byte order ( sys.byteorder is equal to 'little' on my system — from "low to high"). After calling .byteswap() byte order was changed to "from .byteswap() to low", which changed the stored values ​​(which are always interpreted in sys.byteorder ):

 >>> import sys >>> 1 .to_bytes(2, sys.byteorder) b'\x01\x00' # от "младшего к старшему" >>> 1 .to_bytes(2, 'big') b'\x00\x01' # "от старшего к младшему" >>> int.from_bytes(_, 'little') # интепретируя как от "младшего к старшему" 256 

The same API works for numpy arrays:

 >>> import numpy >>> a = numpy.array([1, 32767], dtype='h') >>> a array([ 1, 32767], dtype=int16) >>> a.tobytes() b'\x01\x00\xff\x7f' >>> a.byteswap(True) # inplace array([ 256, -129], dtype=int16) >>> a.tobytes() b'\x00\x01\x7f\xff' 

It is seen that identical results are obtained.


If you have a Python list with Python numbers [1, 32767] , then the concept of byte order does not make much sense. As a matter of fact, Python int in memory is represented by implementation and for most applications it does not matter. For example, in CPython, int consists of sys.int_info.bits_per_digit -bit digits, each of which takes sys.int_info.sizeof_digit bytes, where these digits go from the lowest to the highest position and the absolute value of Python int is :

 SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i) 

    A bit of magic with numbers. First, we divide the two-byte value into two single-byte ones:

     lo = value % 256 hi = value // 256 

    Now we collect back to two-byte:

     value == hi * 256 + lo # всегда должно быть верно rev_value = lo * 256 + hi # то, что хотим по условию, поменяли байты 

    As a result, if you write shorter as a function:

     rev_bytes = lambda v: v % 256 * 256 + v // 256 

    Applying this function to all elements of the list, for example, using map , we get a complete solution.

    Remark 1 : To increase performance, this should be written through logical bitwise operations, replacing i // 256 with i >> 8 , i * 256 with i << 8 , and + with | :

     rev_bytes = lambda v: ((v % 256) << 8) | (v >> 8) 

    Warning : be careful with the order of the operations, you can get an unexpected result if you put the brackets in the wrong way or not at all.

    Note 2 : Another possible optimization here is memorialization. If this function needs to be applied many times, it will be faster to save its results for all two-byte numbers into an array once, and to replace the function call by taking the necessary element from this array.


    UPD:

    Let MyArray be a list of two-byte numbers stored in an int . Then the new array will be like this:

     NewArray = list(map(lambda v: ((v % 256) << 8) | (v >> 8), MyArray)) 

    map apply the function of changing bytes in places to each element of MyArray . list will make a list of it.

    • one
      If not difficult, write the full code - Denis Leonov
    • 1- What type of value ? Python int is not a "double-byte word". 2- It’s not worth talking without measurements (even C is not clear if there is a difference, not to mention Python) 3- mention explicitly the behavior for negative numbers 4- v % 256 as v & 0xFF write. - jfs
     def reverse(input): L = len(input) if L%2 <> 0: return 'Error' #Ошибка в случае когда L не кратно 2 else: Res = '' L = L//2 for i in range(L): T = input[i*2] + input[i*2+1] Res = T + Res T = '' return(Res); 

    Arr - your array of numbers (array of strings, each line of the form A1F6... , in your case A65D , for example)

    Call code:

     ArrLength = len(Arr) for i in range(ArrLength): Arr[i] = reverse(Arr[i]) 
    • one
      You need to tweak the code a bit: replace <> with != . And it would be good to screw the conversion to hex and back with adding the necessary number of zeros to the beginning of the line. - diversenok
    • @diversenok then you have to somehow figure out which side to add zeros. Is not it? - Denis Leonov
    • one
      Just two-byte numbers written as strings are not always 4 characters long. Ok, I'll write myself: IntToHex = lambda a: '0' * (6 - len(hex(a))) + hex(a)[2:] - diversenok
    • My - the easiest option) - Denis Leonov

    You can try to "serialize" the Python element into a binary binary representation, then swap bytes.

    I assume that the elements are stored as numbers.

     from struct import pack, unpack def reorder_bytes( value ): return unpack( "H", pack( "H", value )[::-1] )[0] # модуль struct используется для конвертации С структур в python объекты # H --- unsigned int16 # pack с "H" сконвертирует python int в бинарную строку длиной в 2 байта, соответствующую Си представление unsigned int16 # unpack возвращает tuple из распакованных данных, нужный нам результат будет в первом элементе. 
    • This function returns a tuple with one element, it would be nice to unpack it. - diversenok
    • Added unpacking. - Arnial