Rapidjson with gzstream lib last char '-1'

rapidjson with gzstream lib last char '-1'

本文关键字:char last with gzstream lib Rapidjson      更新时间:2023-10-16

我已经编写了gzstream 1.5的简单包装器,用于rapidjson 0.1(ios,xcode 6.1)。

问题:我必须在 Peek() 和 Take() 中检查 eof。否则,我得到"\377"(-1)作为最后一个字符。我知道它是由 std::basic_stream:get() 在 eof 返回的。

还有什么更优雅、更合适、更干净的解决方案?

class GzOutStream {
public:
    GzOutStream(std::string filename) : gs_(new ogzstream(filename.c_str())) {}
    bool Good() { return gs_->good(); }
    void Close() { delete gs_; gs_ = nullptr; }
    size_t Tell() { return gs_->tellp(); }
    void Put(char c) { gs_->put(c); }
    // Not implemented
    char* PutBegin() { return 0; }
    size_t PutEnd(char*) { return 0; }
private:
    ogzstream* gs_;
};
class GzInStream {
public:
    GzInStream(std::string filename) : gs_(new igzstream(filename.c_str())) {}
    bool Good() { return gs_->good(); }
    void Close() { delete gs_; gs_ = nullptr; }
    char Peek() { return gs_->eof()? '' : gs_->peek(); }
    char Take() { return gs_->eof()? '' : gs_->get(); }
    size_t Tell() { return gs_->tellg(); }
    void Put(char c) { } // Stab
    // Not implemented
    char* PutBegin() { return 0; }
    size_t PutEnd(char*) { return 0; }
private:
    igzstream* gs_;
};

以下答案用于对手头问题的一般性讨论。当时我没有查到rapidjson的行为。

你的类旨在成为gzip输入流和rapidjson之间的粘合逻辑,所以你必须实现rapidjson期望的接口。它甚至没有良好的功能。rapidjson 期望的接口在 EOF 上返回"\0",所以这是你必须做的唯一选择。如果您使用的 gzip 流类正在实现C++流模型,则可以使用 https://github.com/miloyip/rapidjson/blob/master/doc/stream.md"示例 istream 包装器"部分中描述的模式,该模式以通常适用于C++ iostream 的方式执行 EOF 检测。如果您当前的方式适用于 gz 流,您也可以保持原样。


基本上遇到了输入流保持良好的问题,只要您不尝试通过 eof。GzInStream 的接口不为用户提供任何在 Peek 或 Take 返回无效值之前检测 EOF 是否已被命中的可能性。这是由于C++ iostream 的设计:大多数时候,低级 API 不会指示"流的结束",除非您尝试通过它,因此高级 API 不提供此功能,因为在许多(非文件)情况下实现它并不容易。

标准 C++ iostream 的 peek() 和 get() 函数返回 int 而不是 char 是有原因的:它们被指定为以正数量返回从流中读取的字节(在具有 8 位字节的系统上为 0..255),同时在出错时返回 eof (-1)。您的 Peek 和 Get 函数无法返回 256 个不同的字节和 EOF 作为不同的返回值,因为 char 无法表示 257 种可能性。因此,就目前而言,您的界面的客户端在从 Peek 或 Take 获取字符必须询问"Good()",以了解是否真的有要获取的字符。如果接口的客户端这样做,则返回"\377"或"\0"或任何其他值都无关紧要,因为无论如何都会忽略该值。使用该"额外"字节的客户端(在我看来)有问题,除非它被设计为忽略您返回的虚假 NUL 字节。

您可以通过不同的方式解决此问题

  • 如上所述修复客户端,并记录该类的行为。
  • 有 Good() 返回gs_->good() && !gs_->eof(),在过度阅读 eof 之前依赖于 gs_->eof() 是真的
  • 从 Peek 和 Take 返回一个 int,就像标准的 iostream 一样。
  • 从 Peek 和 Take 返回 boost::可选,如果遇到 eof,则返回 boost::none
  • 抛出一个例外从偷看和采取,以防 EOF。

大多数人会立即拒绝最后一个提议的修复程序,因为它违反了"不应将例外用于流控制"规则。我同意强制客户端使用异常处理来取消 EOF 是非常糟糕的风格,但这实际上是唯一不需要更改 Peek 和 Take 的签名或更改其他函数语义的可能性。我希望第二个建议(更改 Good)是您用例中的方式。