Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

read part of a file with iostreams

Tags:

c++

iostream

Can I open an ifstream (or set an existing one in any way) to only read part of a file? For example, I would like to have my ifstream read a file from byte 10 to 50. Seeking to position 0 would be position 10 in reality, reading past position 40 (50 in reality) would resualt in an EOF, etc. Is this possible in any way?

like image 364
Baruch Avatar asked Jan 22 '12 20:01

Baruch


2 Answers

It definetly can be done by implementing a filtering stream buffer: you would derive from std::streambuf and take the range you want to expose and the underlying stream buffer (well, a pointer to it) as arguments. Then you would seek to the start location. An overridden underflow() function would read from the underlying stream buffer into its buffer until has consumed as many characters as were desired. Here is a somewhat rough and entirely untested version:

#include <streambuf>
struct rangebuf: std::streambuf {
    rangebuf(std::streampos start,
                    size_t size,
                    std::streambuf* sbuf):
        size_(size), sbuf_(sbuf)
    {
        sbuf->seekpos(start, std::ios_base::in);
    }
    int underflow() {
        size_t r(this->sbuf_->sgetn(this->buf_,
            std::min<size_t>(sizeof(this->buf_), this->size_));
        this->size -= r;
        this->setg(this->buf_, this->buf_, this->buf_ + r);
        return this->gptr() == this->egptr()
            ? traits_type::eof()
            : traits_type::to_int_type(*this->gptr());
    }
    size_t size_;
    std::streambuf* sbuf_;
};

You can use a pointer to an instance of this stream buffer to initialuze an std::istream. If this a recurring need, you might want to create a class derived from std::istream setting up the stream buffer instead.

like image 120
Dietmar Kühl Avatar answered Sep 21 '22 09:09

Dietmar Kühl


You could read the bytes that you want into a string or char array, then you can use that string with a istringstream, and use that instead of your ifstream. Example:

std::ifstream fin("foo.txt");
fin.seekg(10);
char buffer[41];
fin.read(buffer, 40);
buffer[40] = 0;
std::istringstream iss(buffer);
for (std::string s; iss >> s; ) std::cout << s << '\n';

If you need to handle binary files, you can do that too:

std::ifstream fin("foo.bin", std::ios::binary | std::ios::in);
fin.seekg(10);
char buffer[40];
fin.read(buffer, 40);
std::istringstream(std::string(buffer, buffer+40));
like image 44
Benjamin Lindley Avatar answered Sep 22 '22 09:09

Benjamin Lindley