panc_fab
panc_fab

Reputation: 530

C++ - Read .gz file, uncompress it and write to another file

I'm working on a project that requires reading from files. Those files can be uncompressed or compressed (.gz extension).

I've managed to set my project to exploit zlib and boost::iostreams to perform the uncompression, but I cannot properly handle the uncompressed files in form of std::istream.

Let me be more clear: the method I'm writing has the path to the file as input, and the logic that reads the file is done on std::ifstream object type. If the file is uncompressed one I'm ok, but the problem arises when it is compressed. Following online documentation from boost website, I'm doing something like this

//Read from the first command line argument, assume it's gzipped
std::ifstream file(filename, std::ios_base::in | std::ios_base::binary);
boost::iostreams::filtering_streambuf<boost::iostreams::input> inbuf;
inbuf.push(boost::iostreams::gzip_decompressor());
inbuf.push(file);
//Convert streambuf to istream
std::istream instream(&inbuf);

The problem is that I want to have a std::ifstream for the uncompressed file, but the only way in which I can get the uncompressed file is in form of std::istream (last line of the snippet). Because my handling logic is expecting std::ifstream, I am trying to figure out how I can get an ifstream from the istream, but so far I've found no useful resources to start with.

Rewriting logic to handle different input type is not the goal that I want, so I'm asking you if you have any hint on the topic.

Any help/resource appreciated!

Thanks

Upvotes: 1

Views: 1940

Answers (1)

sehe
sehe

Reputation: 392911

If you need a filestream, make it a file.

I'd argue that the API that requires a ifstream& instead of istream& is probably wrong, because there's rarely a reason to assume such things about your istream& - ifstream, istringstream etc can all be passed just fine.

As long as the API is like that, you will need a file:

Live On Coliru

#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <fstream>

namespace io = boost::iostreams;

int main(int argc, char** argv) {
    assert(argc>1);
    // Read from the first command line argument, assume it's gzipped
    std::ifstream file(argv[1], std::ios::binary);
    io::filtering_istreambuf inbuf;
    inbuf.push(io::gzip_decompressor());
    inbuf.push(file);

    // Convert streambuf to istream
    {
        std::ofstream ofs("temp.dat", std::ios::binary);
        io::copy(inbuf, ofs);
    }
    std::ifstream instream("temp.dat");
}

For the test run

g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp -lboost_iostreams
gzip -k main.cpp
./a.out main.cpp.gz
md5sum temp.dat main.cpp

Prints

6e2d99a75e7f637e9c07e9c78acb04f4  temp.dat
6e2d99a75e7f637e9c07e9c78acb04f4  main.cpp

Upvotes: 1

Related Questions