Equivalent C union in python?

二次信任 提交于 2021-01-03 07:07:22

问题


Say I'm having a following code in C

union u_type
{
    uint32_t data;
    uint8_t  chunk[4];
} 32bitsdata;

32bitsdata.chunk[0] = some number;
32bitsdata.chunk[1] = some number;
32bitsdata.chunk[2] = some number;
32bitsdata.chunk[3] = some number;

printf("Data in 32 bits: %d\n", 32bitsdata.data);

How could I do similar thing in python?

I'm trying to read a binary file (byte by byte) - already got it working, and combining every 3 bytes into one int. Heard struct would do the trick, but I'm not really sure how.

Best,

Henry


回答1:


What about ctypes?

from ctypes import (
        Union, Array, 
        c_uint8, c_uint32, 
        cdll, CDLL
) 

class uint8_array(Array):
    _type_ = c_uint8
    _length_ = 4

class u_type(Union):
    _fields_ = ("data", c_uint32), ("chunk", uint8_array)

# load printf function from Dynamic Linked Libary libc.so.6 (I'm use linux)
libc = CDLL(cdll.LoadLibrary('libc.so.6')._name)
printf = libc.printf

if __name__ == "__main__":
    # initialize union
    _32bitsdata = u_type()
    # set values to chunk
    _32bitsdata.chunk[:] = (1, 2, 3, 4)
    # and print it
    printf(b"Data in 32 bits: %d\n", _32bitsdata.data)



回答2:


Here is what you would do. First, let's create the raw bytes we need, I'll cheat and use numpy:

>>> import numpy as np
>>> arr = np.array((8,4,2,4,8), dtype=np.uint32)
>>> arr
array([8, 4, 2, 4, 8], dtype=uint32)
>>> raw_bytes = arr.tobytes()
>>> raw_bytes
b'\x08\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x08\x00\x00\x00'

These could have easily been read from a file. Now, using the struct module is trivial. We use the unsigned int format character 'I':

>>> import struct
>>> list(struct.iter_unpack('I', raw_bytes))
[(8,), (4,), (2,), (4,), (8,)]

Note, each time we iterate we get back a tuple, since our struct has one member, it is a list of singleton tuples. But this is trivial to get into a flat python list:

>>> [t[0] for t in struct.iter_unpack('I', raw_bytes)]
[8, 4, 2, 4, 8]

Another alternative is to read them into an array.array:

>>> import array
>>> my_array = array.array('I', raw_bytes)
>>> my_array
array('I', [8, 4, 2, 4, 8])



回答3:


You asked about C union, but if your objective is to group 3 bytes into an int, you could use Python struct.unpack instead.

import struct

chunk = bytearray()
chunk.append(0x00)   # some number
chunk.append(0xc0)   # some number
chunk.append(0xff)   # some number
chunk.append(0xee)   # some number

# Convert to a 32-bit unsigned int.
# You didn't specify the byte-order, so I'm using big-endian.
# If you want little-endian instead, replace the '>' symbol by '<'.
data = struct.unpack('>I', chunk)[0]  # unpack returns a tupple, but we only need the first value

print(hex(data))  # the terminal prints 0xc0ffee



回答4:


If you're doing fancy numerical manipulation, you'd probably want to use the numpy library anyway, so consider the "view" method of numpy's ndarray type. The original ndarray can be viewed and modified via the view-array.

>>> import numpy as np
>>> a = np.uint32([1234567890])
>>> b = a.view(np.uint8)
>>> print(a)
[1234567890]
>>> print(b)
[210   2 150  73]
>>> b[2] = 10
>>> print(*b)
210 2 10 73
>>> print(*a)
1225392850


来源:https://stackoverflow.com/questions/45383771/equivalent-c-union-in-python

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!