Numpy 可用于读取/写入二进制数据。您只需要定义一个自定义np.dtype
实例来定义您的 c-struct 的内存布局。
例如,这里有一些定义结构的 C++ 代码(应该同样适用于 C 结构,尽管我不是 C 专家):
struct MyStruct {
uint16_t FieldA;
uint16_t pad16[3];
uint32_t FieldB;
uint32_t pad32[2];
char FieldC[4];
uint64_t FieldD;
uint64_t FieldE;
};
void write_struct(const std::string& fname, MyStruct h) {
// This function serializes a MyStruct instance and
// writes the binary data to disk.
std::ofstream ofp(fname, std::ios::out | std::ios::binary);
ofp.write(reinterpret_cast<const char*>(&h), sizeof(h));
}
根据我在stackoverflow.com/a/5397638pad16
上找到的建议,我在结构(和字段)中包含了一些填充,pad32
以便以更可预测的方式进行序列化。我认为这是 C++ 的事情;使用普通的 ol' C 结构时可能没有必要。
现在,在 python 中,我们创建一个numpy.dtype
描述内存布局的对象MyStruct
:
import numpy as np
my_struct_dtype = np.dtype([
("FieldA" , np.uint16 , ),
("pad16" , np.uint16 , (3,) ),
("FieldB" , np.uint32 ),
("pad32" , np.uint32 , (2,) ),
("FieldC" , np.byte , (4,) ),
("FieldD" , np.uint64 ),
("FieldE" , np.uint64 ),
])
然后使用 numpyfromfile
读取保存 c-struct 的二进制文件:
# read data
struct_data = np.fromfile(fpath, dtype=my_struct_dtype, count=1)[0]
FieldA = struct_data["FieldA"]
FieldB = struct_data["FieldB"]
FieldC = struct_data["FieldC"]
FieldD = struct_data["FieldD"]
FieldE = struct_data["FieldE"]
if FieldA != expected_value_A:
raise ValueError("Bad FieldA, got %d" % FieldA)
if FieldB != expected_value_B:
raise ValueError("Bad FieldB, got %d" % FieldB)
if FieldC.tobytes() != b"expc":
raise ValueError("Bad FieldC, got %s" % FieldC.tobytes().decode())
...
count=1
上述调用中的参数是np.fromfile(..., count=1)
使返回的数组只有一个元素;这意味着“从文件中读取第一个结构实例”。请注意,我正在索引[0]
以将该元素从数组中取出。
如果您已将来自许多 c-struct 的数据附加到同一个文件中,则可以使用fromfile(..., count=n)
将 struct 实例读n
入 shape 的 numpy 数组(n,)
。设置count=-1
是np.fromfile
andnp.frombuffer
函数的默认值,表示“读取所有数据”,从而生成一个 shape 的一维数组(number_of_struct_instances,)
。
您还可以使用offset
关键字参数来np.fromfile
控制文件中数据读取的开始位置。
总而言之,这里有一些 numpy 函数,一旦定义了自定义,它们就会很有用dtype
:
- 将二进制数据读取为 numpy 数组:
- 将 numpy 数组写入二进制数据:
ndarray.tobytes()
:构造一个bytes
包含来自给定 numpy 数组的原始数据的 python 实例。如果数组的数据具有对应于 c-struct 的 dtype,则来自的字节
ndarray.tobytes
可以由 c/c++ 反序列化并解释为该 c-struct 的(数组)实例。
ndarray.tofile(filename)
: 将数组中的二进制数据写入filename
. 然后可以通过 c/c++ 反序列化此数据。相当于open("filename", "wb").write(a.tobytes())
。