protobuf 压缩

在 github 上我们能找到 protobuf 的 最新发布版本

在发布的压缩包中,我们只需要关注 protobuf-cpp-version.zip/tar.gz。有时 protobuf-version-win32.zip 也会让人疑惑,我们到底需不需要这个文件呢?让我们看一下其中的 readme.txt

This package contains a precompiled binary version of the protocol buffer compiler (protoc). This binary is intended for users who want to use Protocol Buffers in languages other than C++ but do NOT want to compile protoc themselves.

简单说,是给 C++ 开发者之外的码农用的。

但是,If you intend to use the included well known types,我们还是需要下载后者的。毕竟前者中 .proto 原型混杂在了大量的头文件中,而这里的原型文件一目了然。

二进制

在解析文件服务器上的历史 k 线时,碰到了比较特殊的问题:

通过 asio 网络通信 streambuf 解析正常

1
2
3
4
5
6
7
8
9
10
11
void client_k::parsebody(asio::streambuf& buf, yuanda::GluedK& calstick)
{
if (buf.size())
{
//解析k线文件
yuanda::GluedCandleStickCombo combo;
std::istream response_stre(&buf);
if (auto success = combo.ParseFromIstream(&response_stre))
// ... true/success
}
}

但是将文件下载下来,通过 fstream 解析时,个别文件正常,大多数文件在 ParaseFromIstream() 总是解释失败

1
2
3
4
5
6
7
8
9
std::fstream ff;
ff.open(u8R"(F:\_DayK\SH600000\glued_candlestick_SH600000_5_0_2000.data)");
if (ff.is_open())
{
yuanda::GluedCandleStickCombo combo;
std::istream is(ff.rdbuf());
if (auto success = combo.ParseFromIstream(&is))
// ... false/fail
}

进一步调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
std::fstream ff;
ff.open(u8R"(F:\_DayK\SH600000\candlestick_SH600000_5_0_2000.data)");
if (ff.is_open())
{
yuanda::CandleStick calstick;
int headsize = 0, bodysize = 0;
ff.read((char*)&headsize, sizeof(int));
ff.read((char*)&bodysize, sizeof(int));
auto kheader = std::shared_ptr<char>(new char[headsize], [](char* ptr) { delete[] ptr; });
auto kbody = std::shared_ptr<char>(new char[bodysize], [](char* ptr) { delete[] ptr; });
ff.read(kheader.get(), headsize);
assert(ff.gcount() == headsize);
ff.read(kbody.get(), bodysize);
auto s2 = ff.gcount();
assert(s2 == bodysize); // false 断言在此处失败

查看 std::basic_istream::read() 的说明

释出并存储字符,直至出现任何下列条件:

释出并存储了 count 个字符
输入序列上的文件尾条件(该情况下调用 setstate(failbit|eofbit) )。成功释出的字符数能用 gcount() 查询。

问题根源

文件尾 EOF 到底是什么呢?替代字符 \0x1A

二进制与文本模式

默认以文本流打开文件,但文本流如果要保证与先前写入“一致”(与先前写到该文本流者比较相等)是有好多限制的,其中一条就是

数据只由打印字符和控制字符 \t\n 组成(尤其是 Windows OS 上,字符 ‘\0x1A’ 终止输入)

二进制流,从二进制流读取的数据始终与先前写出到该流者比较相等。

POSIX implementations do not distinguish between text and binary streams (there is no special mapping for \n or any other characters)