我编写了简单的gzstream 1.5包装器,用于使用rapidjson 0.1(ios,xcode 6.1)。
问题:我必须在Peek()和Take()中检查eof。否则,我得到'\ 377'( - 1)作为最后一个字符。我知道它在eof上由std :: basic_stream :: get()返回。
什么是更优雅,适当和干净的解决方案?
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()? '\0' : gs_->peek(); }
char Take() { return gs_->eof()? '\0' : 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_;
};
答案 0 :(得分:1)
以下答案用于对手头问题的一般性讨论。那时我没有查看rapidjson的行为。
您的类旨在成为gzip输入流和rapidjson之间的粘合逻辑,因此您有来实现rapidjson期望的接口。它甚至没有良好的功能。 rapidjson期望的界面在EOF上返回'\ 0',所以这是你必须做的唯一选择。如果您使用的gzip流类正在实现C ++流模型,则可以使用“示例istream包装器”一节中https://github.com/miloyip/rapidjson/blob/master/doc/stream.md中描述的模式,该模式以通常与C ++ iostream一起使用的方式执行EOF检测。如果您当前的方式适用于gz流,您也可以保持原样。
只要您没有尝试去过去 eof,您实际上就会遇到输入流保持良好状态的问题。 GzInStream的接口不提供用户在Peek或Take返回无效值之前检测EOF是否被击中的任何可能性。这是由于C ++ iostream的设计:大多数情况下,低级API不指示“流结束”,除非您尝试通过它,因此高级API不提供此功能,因为它非常重要在许多(非文件)案例中实施。
标准C ++ iostream的peek()和get()函数返回int
而不是char
有一个原因:它们被指定为将从流中读取的字节作为正数量返回(0。 .255在具有8位字节的系统上),同时在出错时返回eof(-1)。您的Peek和Get函数无法返回256个不同的字节和EOF作为不同的返回值,因为char无法表示257种可能性。因此,在获取Peek或Take中的角色后,界面的客户必须询问“Good()”,以确定是否确实存在角色。如果您的界面的客户端这样做,无论您返回'\ 377'还是'\ 0'或任何其他值都无关紧要,因为无论如何都会忽略该值。使用“额外”字节的客户端(在我的意见中)是错误的,除非它被设计为忽略您返回的虚假NUL字节。
您可以通过不同方式解决此问题
gs_->good() && !gs_->eof()
,在重读eof之前依赖gs_->eof()
为真大多数人会立即拒绝最后提出的修复,因为它违反了“不应该用于流量控制的例外”规则。我同意强制客户端使用异常处理来处理EOF是非常不好的风格,但这实际上是唯一不需要更改Peek和Take的签名也不会改变其他函数语义的可能性。我希望第二个建议(改变好)是你用例的方法。