insert a string in front of a istream in cpp
Asked Answered
M

2

4

My problem is something like I want to append some string in front of a iostream. You can say in front of std::cin.

#include <iostream>
#include <string>

void print(std::istream & in){// function not to be modified
    std::string str;
    in >> str;
    std::cout<< str << std::endl;
    in >> str;
    std::cout<< str << std::endl;
}

int main() {
    std::string header = "hello ";
    //boost::iostream::filtering_istream in(std::cin);
    std::istream & in = std::cin;

    //you may do someting here
    //something like inserting characters into cin or something with buffers

    print(in);
}

I want the implementation of the fuction such that if I provide input like

$ cat file.txt
help me to solve this.
$
$ ./a.out < file
hello
help
$

any kind of help is welcome. you may use boost::iostream to implement it.

Messuage answered 20/3, 2018 at 23:7 Comment(8)
"My problem is something like I want to append some string in front of a iostream." No, this is how you think you need to solve your problem. Tell us the actual problem.Lascar
Boost's filters may be a good way to perform the restructuring I suggest in my answer. I invite you to try implementing it first :PLascar
@LightnessRacesinOrbit My actual problem is that I want to read some characters from stream and put them back into the stream for later use.Messuage
Again, no, you've merely described what you think the solution is. Never mind it's bedtime for meLascar
No, I cannot do that. Make the appropriate edits to your question so that all may benefit.Lascar
@LightnessRacesinOrbit my question is extension of #49182820 .. as you can notice in the comments that there it is mentioned to use unget in boost. My doubt is that using multiple ungets in not treated as safe thing. Is it safe to use multiple ungets in boost::filtering streams ?Messuage
That's a completely different question.Lascar
@LightnessRacesinOrbit actually it is related. the requirement is same. I am working on reading a compressed file. I have to read it using boost filters. but without knowing compression type I cannot apply filters. I need to read few characters to apply filter. and if I read those characters to apply filter then I too need to put them back into stream before applying filterMessuage
L
3

A stream is not a container. It is a flow of data. You cannot change data that has already floated away. The only exception is streams tied to block devices, in which you can seek around, e.g. fstream - but even then you can only overwrite.

Instead, structure your code so that header is consumed before the stream is consulted at all.

Lascar answered 20/3, 2018 at 23:9 Comment(0)
F
2

You should avoid the issue. Just don't parse it all from the same stream if it isn't the same stream.

Low-Tech

A low-tech "solution" is to copy the stream to a stringstream, putting your "prefix" in the buffer first:

Live On Coliru

#include <iostream>
#include <string>
#include <sstream>

void print(std::istream &in) { // function not to be modified
    std::string str;
    while (in >> str)
        std::cout << str << std::endl;
}

int main() {
    std::string header = "hello ";
    std::stringstream in;
    in << header << std::cin.rdbuf();

    print(in);
}

Given the input foo bar qux prints:

hello
foo
bar
qux

High-Tech

You can always create a custom stream buffer that implements the behaviour you desire:

Live On Coliru

#include <iostream>
#include <sstream>
#include <vector>

template <typename B1, typename B2>
class cat_streambuf : public std::streambuf {
    B1* _sb1;
    B2* _sb2;
    std::vector<char> _buf;
    bool _insb1 = true;

  public:
    cat_streambuf(B1* sb1, B2* sb2) : _sb1(sb1), _sb2(sb2), _buf(1024) {}

    int underflow() {
        if (gptr() == egptr()) {
            auto size = [this] {
                if (_insb1) {
                    if (auto size = _sb1->sgetn(_buf.data(), _buf.size()))
                        return size;
                    _insb1 = false;
                }

                return _sb2->sgetn(_buf.data(), _buf.size());
            }();

            setg(_buf.data(), _buf.data(), _buf.data() + size);
        }
        return gptr() == egptr() 
            ? std::char_traits<char>::eof()
            : std::char_traits<char>::to_int_type(*gptr());
    }
};

void print(std::istream &in) { // function not to be modified
    std::string str;
    while (in >> str)
        std::cout << str << std::endl;
}

int main() {
    std::stringbuf header("hello ");
    cat_streambuf both(&header, std::cin.rdbuf());

    std::istream is(&both);
    print(is);
}

Which also prints

hello
foo
bar
qux

for the same input. This will definitely scale better for (very) large streams.

Fragonard answered 23/3, 2018 at 1:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.