在Windows上从文件读取多个分隔的protobuf消息

我正在为我的硕士论文写一个工具,需要从文件中读取protobuf数据stream。 到现在为止,我专门在Mac OS上工作,一切都很好,但现在我试图在Windows上运行该工具。

不幸的是,在Windows上,我无法从单个stream中读取多个连续的消息。 我试图缩小这个问题的范围,然后来到下面这个重现问题的小程序。

#include "tokens.pb.h" #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/io/zero_copy_stream_impl.h> #include <fstream> int main(int argc, char* argv[]) { std::fstream tokenFile(argv[1], std::ios_base::in); if(!tokenFile.is_open()) return -1; google::protobuf::io::IstreamInputStream iis(&tokenFile); google::protobuf::io::CodedInputStream cis(&iis); while(true){ google::protobuf::io::CodedInputStream::Limit l; unsigned int msgSize; if(!cis.ReadVarint32(&msgSize)) return 0; // probably reached eof l = cis.PushLimit(msgSize); tokenio::Union msg; if(!msg.ParseFromCodedStream(&cis)) return -2; // couldn't read msg if(cis.BytesUntilLimit() > 0) return -3; // msg was not read completely cis.PopLimit(l); if(!msg.has_string() && !msg.has_file() && !msg.has_token() && !msg.has_type()) return -4; // msg contains no data } return 0; } 

在Mac OS上,它运行正常,并按照我的预期读取整个文件后返回0。

在Windows上读取第一条消息没有问题。 对于第二个消息ParseFromCodedInputStream仍然返回true,但不读取任何数据。 这会导致BytesUntilLimit值大于0,返回值为-3。 当然这个消息也不包含任何可用的数据。 即使文件还没有被完全读取,任何从cis读取的进一步读取也将失败,就好像stream已经达到了结束一样。

我也尝试使用带有文件描述符的FileInputStream来input相同的结果。 删除Push/PopLimit并使用显式消息大小的ReadString调用读取数据,然后从该stringparsing也没有帮助。

以下protobuf文件被使用。

 package tokenio; message TokenType { required uint32 id = 1; required string name = 2; } message StringInstance { required string value = 1; optional uint64 id = 2; } message BeginOfFile { required uint64 name = 1; optional uint64 type = 2; } message Token { required uint32 type = 1; required uint32 offset = 2; optional uint32 line = 3; optional uint32 column = 4; optional uint64 value = 5; } message Union { optional TokenType type = 1; optional StringInstance string = 2; optional BeginOfFile file = 3; optional Token token = 4; } 

这是一个示例input文件 。

input文件似乎没问题。 至less可以通过protobuf编辑器(在Windows和Mac OS上)以及Mac OS上的c ++实现来读取。

代码已经过testing:

  • 正在使用Mac OS 10.8.4,使用Xcode 4.6.3和protobuf 2.5.0编译
  • 由于没有在Windows 8 64Bit上工作,使用Visual Studio 2012 Ultimate和protobuf 2.5.0编译

我究竟做错了什么?

使它std::fstream tokenFile(argv[1], std::ios_base::in | std::ios_base::binary); 。 默认是文本模式; 在Mac和其他类Unix系统上,这并不重要,但是在文本模式下,您可以将CRLF序列转换为LF,^ Z(又名'\ x1A')字符视为文件结束指示符。 这些字符可能巧合地出现在二进制流中,并造成麻烦。