无法使用 C++ ifstream 正确解析二进制文件

问题描述 投票:0回答:2

我正在尝试使用 C++ 解析 NASDAQ ITCH 协议数据转储。这些文件很大,任何有兴趣的人都可以在这里找到:

ftp://emi.nasdaq.com/ITCH/

这些文件的规格归结为:

  1. 两个字节的大端长度,指示数据包其余部分的长度
  2. 指示类型的单字节 ASCII 标头
  3. 可变长度有效负载(大小: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
c++ parsing binary ifstream
2个回答
1
投票

@Retired Ninja 正确指出该文件已被 gzip 压缩,这是错误的根源。我基本上只是错误地掩盖了这样一个事实:在 python 中我正在调用特定于 gzip 的 io 读取器。解压文件后,我可以正确解析第一个字节。


0
投票

尝试解压缩文件。压缩文件具有不同的二进制编码消息,解压后它必须正常工作。

© www.soinside.com 2019 - 2024. All rights reserved.