В настоящее время у меня есть код, который по существу содержит эти команды:
std::string line;
std::getline(ifs, line);
ifs
- это немного std::ifstream
. Код работает.
Теперь я хотел бы использовать то же самое для данных, которые я распаковываю из другого источника. По сути, я читал порциями, которые я где-то буферизировал, а затем извлекал данные из этого буфера, используя функцию, которая немного похожа на fgetc
. Это все работает, но это плохая смесь не STL с кодом STL.
Я хотел бы инкапсулировать декомпрессию и предоставить итератор, чтобы вызов std::getline
работал. Я думаю, это будет istream_iterator
? Какой минимум должен содержать этот класс?
ОБНОВЛЕНИЕ : На основании полученных комментариев и реализации streambuf на http://www.voidcn.com/article/p-vjnlygmc-gy.html, Теперь у меня есть следующий код, который работает. В качестве тестового примера я использовал сжатие zlib. Код не очень хороший, и я ценю любые советы по его улучшению.
class gzip_streambuf: public std::streambuf
{
private:
gzip_streambuf(const gzip_streambuf &);
gzip_streambuf &operator= (const gzip_streambuf &);
public:
explicit gzip_streambuf(gzFile h_, std::size_t buf_sz_ = 1024, std::size_t putback_sz_ = 2) :
putback_sz(std::max(putback_sz_, std::size_t(1))),
buffer(std::max(putback_sz_, buf_sz_ ) + putback_sz_),
h(h_)
{
char *end = &buffer.front() + buffer.size();
setg(end, end, end);
}
~gzip_streambuf() {}
bool error(void)
{
return gzerr;
}
private:
int_type underflow()
{
if (gptr() < egptr())
return traits_type::to_int_type(*gptr());
char *base = &buffer.front();
char *start = base;
if (eback() == base) {
std::memmove(base, egptr() - putback_sz, putback_sz);
start += putback_sz;
}
int n = gzread(h, start, buffer.size() - (start - base));
if ( n < 0 )
gzerr = true;
if ( n <= 0)
return traits_type::eof();
setg(base, start, start + n);
return traits_type::to_int_type(*gptr());
}
private:
bool gzerr = false;
gzFile h = nullptr;
const std::size_t putback_sz;
std::vector<char> buffer;
};
class gzip_istream: public std::istream
{
public:
gzip_istream() {};
~gzip_istream() { delete gzbuf; };
private:
gzip_istream(const gzip_istream &);
gzip_istream &operator= (const gzip_istream &);
public:
void open( const char * filename) {
if ( h = gzopen(filename, "rb") ) {
delete gzbuf;
if ( gzbuf = new gzip_streambuf(h) ) {
set_rdbuf(gzbuf);
clear();
} else
close();
}
}
void close() {
if (h)
gzclose( std::exchange(h, nullptr) );
delete std::exchange(gzbuf, nullptr);
set_rdbuf(nullptr);
}
bool is_open(void) {
return h != nullptr;
}
private:
gzFile h = nullptr;
gzip_streambuf *gzbuf = nullptr;
};
Может использоваться следующим образом:
std::string line;
gzip_istream gin;
gin.open("file.gz");
if (gin.good()) {
while (std::getline( gin, line))
std::cout << line << std::endl;
gin.close();
}
Я пытался использовать std::unique_ptr
для gzip_streambuf
члена в gzip_istream
, но все попытки не увенчались успехом из-за отсутствия конструктора копирования. Я хотел бы избавиться от нового / удалить материал, но я не уверен, как.