-1

So I have several files that form a contingent number of lines, so let's call them file1, file2, file3 and so forth. What I want to do is to create a istream class which streams all the files as if they were one. One thing i got is not to subclass std::istream but to reimplement streambuf. My question is: how would I go about this - how would i be able to read from multiple files without having them all in memory?

Kai
  • 376
  • 1
  • 4
  • 17
  • _"but to reimplement streambuf"_ I doubt that it's reasonably possible with that restriction. But ¯\_(ツ)_/¯ who knows ... – πάντα ῥεῖ Mar 27 '19 at 18:55
  • it is not clear how you want to read from several files. how to wrap that into a `std::istream` interface is kind of secondary before you know how to read from multiple files and what to do with their contents – 463035818_is_not_an_ai Mar 27 '19 at 18:57
  • "how would i be able to read from multiple files without having them all in memory" - I doubt you will be able to, without having at least *parts* of them in memory. How would you read something that's not available for you to read? – Jesper Juhl Mar 27 '19 at 18:59
  • I'm struggling a bit myself, this might not make sense. So here is a preliminary "idea" how it could work: create a buffer, stream the file into the buffer, and if one file is done and the buffer has space left - start loading the other file into it. I am kind of dubious about the idea myself and ask myself if this is good design, hence the ask. – Kai Mar 27 '19 at 18:59
  • in what order do you want to read the contents? if it is one file after the other, you dont have to write anything yourself – 463035818_is_not_an_ai Mar 27 '19 at 19:00
  • Did I miss something obvious? I want to read: file1start -- file1end file2start--file2end file3start -- ... and so forth, you get the idea. – Kai Mar 27 '19 at 19:02
  • why cant you just read till the end then open the next? I dont understand why you think you need to "create a istream class" or what that is supposed to mean exactly. What problem are you actually trying to solve? Can you show some example code? – 463035818_is_not_an_ai Mar 27 '19 at 19:15
  • I'm asking myself the same question, you seem to have the same suspicions - code is currently not available, but that part is in it's infancy anyway. I have to give a istream of some kind to another third party object - so could I just: 1.create an ifstream 2.stream file1 3.close,clear,open file2 4. stream file2 and so forth? – Kai Mar 27 '19 at 19:18
  • If you just want to read from the files sequentially, an easier approach might be to just concatenate them together from the shell, e.g. `cat file0 file1 file2 | ./your_program` (replace `cat` with `type` for windows). This will work exactly as you want; it won't keep everything in memory at once. – Ray Mar 27 '19 at 20:12
  • Similar question (asked in 2013): [Composite std::istream in C++](https://stackoverflow.com/q/19506444/427158) – maxschlepzig Mar 28 '21 at 18:13

1 Answers1

1

Is it possible to make an istream that reads multiple files?

Yes, but you'll have to make it yourself. You can implement your own istream class by extending std::istream, and then implementing the methods it defines:

class custom_istream : public std::istream {
    std::ifstream underlying; 
    //... implement methods
};

The interface of std::istream is sufficiently flexible to allow you to do what you want. However, implementing std::istream would require a lot of work, as you'd have to implement the entirety of the interface.

Is there an easier solution?

If you only need a subset of the functionality provided by std::istream, you could just write your own class.

For example, if you only need to be able to read lines from the file, the below class will work just fine for multiple files:

class MultiFileReader {
    std::ifstream filestream; 
    std::ios_base::openmode mode;  
    size_t file_index = 0; 
    std::vector<std::string> filenames; 
    void open_next_file() {
        file_index++; 
        filestream = std::ifstream(filenames.at(file_index), mode); 
    }
   public:
    MultiFileReader(std::vector<std::string> const& files, std::ios_base::openmode mode)
      : filestream(files[0], mode), mode(mode) {}
    // Checks if there's no more files to open, and no more to read
    // in the current file
    bool hasMoreToRead() {
        if(file_index == filenames.size()) return false;
        if(file_index + 1 == filenames.size()) 
            return not filestream.eof(); 
        return true;
    }
    std::string read_line() {
        if(not hasMoreToRead()) {
            throw std::logic_error("No more to read"); 
        }
        // If at the end of the file, open the next file
        if(filestream.eof()) {
            open_next_file(); 
        }
        else {
            std::string line; 
            std::getline(filestream, line);
            return line; 
        }
    }
};  
Alecto Irene Perez
  • 10,321
  • 23
  • 46
  • Implementing a custom `std::streambuf`/`std::filebuf` would likely be easier/better than implementing a custom `std:istream`. When the stream buffer encounters an EOF while reading in data, it can simply open the next file and keep reading, the `istream` won't know the difference. – Remy Lebeau Mar 27 '19 at 20:57
  • I'll definitely try it, thank you both! Anyone know of a verbose description of the interface of streambuf? – Kai Mar 28 '19 at 09:36