简体   繁体   中英

C++ std::istream to read from file

I am completely new to the c++ and I have problem with managing input. I have a program where I want to be possible to read input from console but also from the file. I have class with private field std::istream &input_stream; and in constructor I set this input_stream as std::cin , however when my program is run with flag -i-FILENAME I want input_stream to be that file.

class FlagsParser
{
private:
    std::istream &input_stream;

public:
    FlagsParser() : input_stream(std::cin){}
    void ParseFlags(int count,char *flags[]) -> arguments I get from main
    {
        for(int i =1;i<count;++i){
            std::string flag = flags[i];
            if(flag.rfind("-i",0) == 0){ // check if the arg starts with -i
                std::ifstream readFile(flag.substr(2)); 
                -> Here i want to set input_stream to readFile and I have no idea how.
                ...


What should I do? Thank you for any help. (Maybe I am doing it completely wrong?)

It is possible, but requires cautious programming.

As input_stream has to be a reference, it must refere to an existing object. There is no problem with std::cin which is a well known object defined by the standard library. To to have it refere a file stream, that damned file stream shall exist somewhere, and potentially different FlagParser objects could have different streams. A simple way to solve that is to add an auxilliary std::ifstream (a true object and not a ref) into the class and declare it before input_stream to be able to initialize input_stream with it. You could then decide to use it if it is an open stream, and use std::cin if it is not.

If the initialization of the ifstream is not trivial, it can be delegated to a member function:

class FlagsParser
{
private:
    std::ifstream inner_stream;
    std::istream &input_stream;

    // this method is expected to be called before full construction
    // of the object. It is not required to be static but it SHALL NOT
    // use any non static member. Making it static guarantees that.
    /* static */ std::ifstream build_stream(int count,char *flags[]) {
        std::ifstream ifs;
        // compute the file path from the args
        if (...) {
            ifs.open(path);
        }
        return ifs;
    }
public:
    Parser(int count, char *argv[]) :
            inner_stream(build_stream(count, argv),
            input_stream(inner_stream.is_open() ? inner_stream : std::cin) {
        // further initialization if required...
    }
};

You can then define your FlagParser in main:

int main(int argc, char *argv[]) {
    FlagParser parser(argc, argv);
    ...
}

if you want two tasks to be run at the same time simultaneously and their output and resource you want to share in between that two tasks then you are doing completely wrong instead you should use the thread concept in c++ std 11

You can make the constructor parametric like this:

public:
    FlagsParser(std::istream& s) : input_stream(s) {}

and then pass the std::cin or std::ifstream (or any other std::istream) as a parameter:

FlagsParser p(std::cin);

or

std::ifstream s(argv[fileArgIdx]);
FlagsParser q(s);

Of course then you have to check argc and argv and choose either this or that option, like this:

int fileArgIdx = -1; // TODO: lookup the index of the filename arg

if (fileArgIdx == -1) {
    FlagsParser parser(std::cin);
    parser.Parse();
} else {
    std::ifstream stream(argv[fileArgIdx]);
    FlagsParser parser(stream);
    parser.Parse();
}

... which works well but does not look good, so you can use a simple class like this:

class Provider {
    std::auto_ptr<std::istream> ptr;

    public:
        Provider(int argc, char* argv[], int fileArgIdx) {
            if (fileArgIdx != -1)
                ptr.reset(new std::ifstream(argv[fileArgIdx]));
        }

        operator std::istream& () {
            return (ptr.get() == nullptr) ? std::cin : *ptr.get();
        }
};

to provide the correct stream:

int fileArgIdx = -1; // TODO: lookup the index of the filename arg

Provider provider(argc, argv, fileArgIdx);
FlagsParser parser(provider);
parser.Parse();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM