【发布时间】:2021-07-25 05:36:11
【问题描述】:
我正在尝试使用 C++ 解析 NASDAQ ITCH 协议数据转储。这些是大文件,感兴趣的人可以在这里查看:
这些文件的规格归结为:
- 一个两字节的大端长度,指示数据包其余部分的长度
- 指示类型的单字节 ASCII 标头
- 可变长度负载(大小:length-1)
为确保下载的文件内容正常,我使用 python 缓冲 gzip 阅读器快速检查了 python。内容按预期解析:
bin_data = gzip.open('01302020.NASDAQ_ITCH50.gz', 'rb')
message_size_bytes = bin_data.read(2)
message_size = int.from_bytes(message_size_bytes, byteorder='big', signed=False)
message_type = bin_data.read(1).decode('ascii')
record = bin_data.read(message_size - 1)
print("size: " + str(message_size) + " type: " + message_type)
# >>> size: 12 type: S
在这种特殊情况下,message_size 打印出 12,这是正确的值。以下字符S也是正确的。
但是,我自己尝试使用 std::ifstream 复制正确的 python 解析行为都失败了。我什至无法正确读取前 2 个字节(这应该表明剩余的总有效载荷大小为 12)。以下是我的尝试,其中一些目前还只是暗中尝试:
#include <iostream>
#include <fstream>
int main() {
std::string filepath = "/Users/estebanlanter/Documents/Finance/HFT/01302020.NASDAQ_ITCH50.gz";
std::ifstream ifs;
ifs.open(filepath, std::ifstream::in);
std::cout<<"open...."<<std::endl;
// trial A
ifs.clear();
ifs.seekg(0);
int size_a;
ifs.read(reinterpret_cast<char*>(&size_a), 2);
std::cout<<"size: "<<size_a<<std::endl;
// size: 325683999
// trial B
ifs.clear();
ifs.seekg(0);
int size_b;
ifs.read(reinterpret_cast<char*>(&size_b), 2);
size_b = ntohl(size_b);
std::cout<<"size: "<<size_b<<std::endl;
// size: 529203200
// trial C
ifs.clear();
ifs.seekg(0);
int size_c;
ifs.read(reinterpret_cast<char*>(&size_c), 2);
size_c = ntohs(size_c);
std::cout<<"size: "<<size_c<<std::endl;
// size: 8075
// trial D
ifs.clear();
ifs.seekg(0);
uint8_t size_d;
ifs.read(reinterpret_cast<char*>(&size_d), 2);
std::cout<<"size: "<<size_d<<std::endl;
// size:
// trial E
ifs.clear();
ifs.seekg(0);
uint8_t size_e;
ifs.read(reinterpret_cast<char*>(&size_e), 2);
size_e = ntohl(size_e);
std::cout<<"size: "<<size_e<<std::endl;
// size:
// trial F
ifs.clear();
ifs.seekg(0);
uint8_t size_f;
ifs.read(reinterpret_cast<char*>(&size_f), 2);
size_f = ntohs(size_f);
std::cout<<"size: "<<size_f<<std::endl;
// size:
// trial G
ifs.clear();
ifs.seekg(0);
char size_g;
ifs.read(&size_g, 2);
std::cout<<"size: "<<size_g<<std::endl;
// size:
// trial H
ifs.clear();
ifs.seekg(0);
char size_h;
ifs.read(&size_h, 2);
size_h = ntohl(size_h);
std::cout<<"size: "<<size_h<<std::endl;
// size:
// trial I
ifs.clear();
ifs.seekg(0);
char size_i;
ifs.read(&size_i, 2);
size_i = ntohs(size_i);
std::cout<<"size: "<<size_i<<std::endl;
// size:
我做错了什么?如何将前 2 个字节解析为整数,将下一个字节解析为字符?使用 Python 似乎很简单...
我是顺便说一句。在小端 MAC OSX 机器上 - gzip 中的数据是大端。
编辑
正如一些正确指出的那样,int 是支持 2 个字节的错误类型。另外,我用 std::ios::binary 替换了 ifstream 标志。不幸的是,仍然没有打印正确的值...
std::string filepath = "/Users/estebanlanter/Documents/Finance/HFT/01302020.NASDAQ_ITCH50.gz";
std::ifstream ifs;
ifs.open(filepath, std::ios::binary);
std::cout<<"open...."<<std::endl;
ifs.clear();
ifs.seekg(0);
unsigned short size_a;
ifs.read(reinterpret_cast<char*>(&size_a), 2);
std::cout<<"size: "<<size_a<<std::endl;
// size: 35615
ifs.clear();
ifs.seekg(0);
short size_b;
ifs.read(reinterpret_cast<char*>(&size_b), 2);
std::cout<<"size: "<<size_b<<std::endl;
// size: -29921
编辑 2
用户 Casey 指出了使用保证大小类型的良好做法。因为我知道大小由 2 个字节组成(并且总是正数),所以我将大小声明为 uint16_t。但是,仍然没有运气到达 python 解析器返回的数字 12....
int main() {
std::string filepath = "/Users/estebanlanter/Documents/Finance/HFT/01302020.NASDAQ_ITCH50.gz";
std::ifstream ifs;
ifs.open(filepath, std::ios::binary);
std::cout<<"open...."<<std::endl;
ifs.clear();
ifs.seekg(0);
uint16_t size_a;
ifs.read(reinterpret_cast<char*>(&size_a), 2);
std::cout<<"size: "<<size_a<<std::endl;
// size: 35615
【问题讨论】:
-
不要使用
std::ifstream::in,而是使用std::ios::binary。此外,当您读取并将值存储到int时,大小将是4而不是2 -
我从规范中知道二进制文件中的每条消息都以......“一个指示数据包其余部分长度的两字节大端长度”开头。你对 int 大小是正确的,但是我尝试用 short 和 unsigned shorts 替换所有 int,这将是 2 个字节,但仍然没有正确解析的输出。同时标记 std::ios::binary.
-
你改成
std::ios::binary了吗? -
是的,我替换了标志并将 int 替换为 unsigned short。我的控制台现在打印 35615。不幸的是,它仍然不是我预期的 12
-
看来您正在阅读 gzip 压缩文件。在 C++ 中没有自动解压缩文件,您要么必须先解压缩文件,要么使用 zlib 和
gzOpen/gzRead之类的东西。 35,516 是 0x8b1f,它是有效 gzip 标头的开头。
标签: c++ parsing binary ifstream