1、C++的二进制数据
首先任意定义一个结构,注意不要用 std::string 非平凡布局的变量,用 char[] 代替:
struct KLine
{
char code[32]; //!< @brief 代码。如600030.SH。
int date; //!< @brief 日期,格式yyyymmdd
int time; //!< @brief 开始时间,格式hhmmss
float open; //!< @brief 开盘价
float high; //!< @brief 最高价
float low; //!< @brief 最低价
float close; //!< @brief 收盘价
float volume; //!< @brief 成交量。单位:万元。
float value; //!< @brief 成交额。单位:万元。
float vwap; //!< @brief 均价
int ret; //!< @brief 涨跌幅
};
然后使用fwrite
写入到二进制文件,其中第二个 size 参数为单个元素的大小,第三个参数为元素个数:
KLine klines[1024];
FILE* file = fopen("./klines.bin", "wb");
fwrite(klines, sizeof(KLine), sizeof(klines) / sizeof(KLine), file);
再从文件读入,简单使用fread
即可,返回值是读入元素的个数:
KLine klines[1024];
FILE* file = fopen("./klines.bin", "rb");
size_t count = fread(klines, sizeof(KLine), sizeof(klines) / sizeof(KLine), file);
2、Python numpy 数据与二进制数据
Python 的 numpy 要处理二进制数据,需要先定义和 C++一样的数据类型:
import numpy as np
kline_dtype = np.dtype([
("code", "|S32"),
("date", np.int32),
("time", np.int32),
("open", np.float32),
("high", np.float32),
("low", np.float32),
("close", np.float32),
("volume", np.float32),
("value", np.float32),
("vwap", np.float32),
("ret", np.float32),
])
然后就可以用 np.memmap 直接从二进制文件读取一个数据了(如果读取数据且不会修改,这种方法最好):
klines = np.memmap("./klines.bin", dtype=kline_dtype, mode="r")
如果将 mode 修改为 w ,在该数据上做的修改,还会被直接同步到文件,不需要手工干预。如果只想从文件读取数据进行修改,不想要和文件的关联,直接用 fromfile (和下面的 tofile 对应):
klines = np.fromfile("./klines.bin", dtype=kline_dtype)
要将 numpy 数据写到二进制文件也很简单,使用 tofile 函数即可:
klines.tofile("./klines.bin")
3、Python Pandas 保存为 C++可处理的二进制文件
Pandas 的 DataFrame 要保存为 C++可处理的二进制文件,需要先用to_records
转为 numpy 数据,再用 tofile 函数保存。
column_dtypes = {
key: ds[0]
for key, ds in kline_dtype.fields.items()
}
data = df.to_records(index=False, column_dtypes=column_dtypes)
data.tofile("./klines.bin")
4、numpy.dtype 详解
上面可以看到, python 处理和 C++互联互通的二进制数据,最核心的就是定义数据结构 np.dtype
。np.dtype 有三种大的类型:
- 内置类型,比如 np.float64, np.int32 等等。
- 一位数组或多维矩阵,比如
np.dtype(np.int32, (12, 24))
。 - 更复杂的数据结构,比如上面的
kline_dtype
,它可以嵌套包含所有数据类型。
它的通用属性有:
- dtype.itemsize :返回该类型的空间占用大小,比如 float64 是 8 , int32 是 4。
- dtype.byteorder :字节序;和 C++混用时,不需要管。
内置类型有下面属性,这些属性对所有类型都可以用,但内置类型的返回值才有意义:
- dtype.name
- dtype.type
- dtype.kind :一个字节的标识符,比如
float
都是f
。 - dtype.char :一个字节的标识符,比 kind 分得更细,比如
float32
为f
,float64
为d
。 - dtype.str :该类型的字符串表示,比如默认的
float32
为<f4
。
数组矩阵类型,有属性:
- dtype.subdtype :数组里元素的类型和元素大小的二元组。
- dtype.shape :数组或矩阵的大小。
复杂数据结构的属性:
- dtype.names :成员的名字列表。
- dtype.fields :
{name: (subdtype, offset)}
的映射,其中 subdtype 是成员类型, offset 是该成员数据相对于起始点的偏移位置。
Q. E. D.