Moriduri
Moriduri

Reputation: 117

Using cin for keyboard input after processing redirected file input with getline

I know, this question has been treated tons of times.. but I can't make it work anyway.. here I paste some code:

#include <sstream>
#include "header.h"
using namespace std;
using namespace header;

int main(){
    string parent, child, line, node;
    int choose = 0;
    tree_ptr tree_inst = new tree;
    cout << "*** Start ***" << endl;    
    while (getline(cin, line, '\n')){
        istringstream line_stream(line);
        line_stream >> parent;
        if (parent.compare("0") == 0) break;
        while (line_stream >> child) {
            if (!tree_inst->insert(parent, child)) {
                cout << "*** ERROR! ***" << endl;
                tree_inst->visit(tree_inst->root);
                cout << "*** End ***" << endl;
                return -1;
            }
        }
    }
    while (true) {
        cin >> choose;  //<== doesn't wait for the input, just loop forever
                        //    on the default statement of the switch...
        switch (choose) {
            ...
        }
    }
}

I've already tried to insert some cin.sync() cin.clear() cin.ignore() ..etc.. but nothing changed!

Upvotes: 4

Views: 1725

Answers (1)

Christophe
Christophe

Reputation: 73627

The first getline() loop would loop forever, if you don't break it in one way or another. If done in a clean manner, this shouldn't be an issue at all. In fact I could not reproduce your error.

How to analyze the error

So the cause is either a bad sate of cin or non numberic non space input that is still pending. To help you find out, I suggest you add some diagnostic code:

cout << "Enter free lines of text or stop to return to the menu:";
while (getline(cin, line, '\n')) {   // This would loop foreved
    if (line == "stop")              // I added this one to exit
        break;                       // but how do you do ?
}
while (true) {                       // I get no problem here
    cout << "Cin status: failed="    // <===Add this simple diagnostic to 
         << cin.fail() << " bad="    //     check the state of the stream 
         << cin.bad() << " eof=" << cin.eof() << endl;  
    cout << "Next char in decimal is:" // <=== Add this to show 
         << cin.peek() << endl;        //      where in the stream you're hanging
    cout << "Choose:";
    cin >> choose;  
    cout << "selected: " << choose << endl;
}

If your stream state is not clean, despite a cin.clean() , it's because either you closed the stream or typed in an end-of-file code at the console (Ctrl+D or Ctrl+Z depending on system) .

It the stream state is clean but the peeked char is not numeric (i.e. decimal code not between 48 and 57), it's the bad input that sets the stream in a fail state.

Edit: your specific case

Looking at the diagnostic you've provided (fail=1, eof=1), it appears that after the first loop you've already eached the end of the input. So your input fails because there is no further data to read. Your input file on pastebin confirms that the last line is the "0" that you use to exit the first loop.

Following our exchanges, I understand that you've in fact redirected input from the command line (e.g. yourprogramme <yourinput.txt ) and expect your code to switch back to keyboard input once the redirected file input reaches its end. Unfortunately this is not the way it works. If you redirect input from a file, cin will always refer to the redireted file.

To be able to mix file and keyboard input, you need to use <fstream> and more precisely ifstream for reading from the file and keep cin for keyboard entry.

Here is a slightly modified code that takes the filename from the command line but without indirection (e.g. yourprogramme yourinput.txt ):

...
int main(int argc, char**argv)
{
    if (argc != 2) {     // if called from command line wrong arguments
        cerr << "You must provide the name of the data file as command line argument\n";
        return EXIT_FAILURE;
    }
    ifstream file(argv[1]);     // open the file for reading 
    if (!file) {                // if open failed, end the programme
        cerr << "Could not open "<<argv[1]<<"\n";
        return EXIT_FAILURE;
    }
    string line;                // here your code from before
    int choose = 0;
    cout << "*** Loading "<<argv[1]<< " ***" << endl;
                              // but in the first loop replace cin with file
    while (getline(file, line, '\n')){   
        cout << "Read: " << line<<endl;
        if (line == "0")                // Abridged version;-)
            break;
    }
    file.close();               // File is no longer needed here.  
    while (true) {              // Second loop, unchanged, using cin  
        cout << "Choose (9 to exit):";
        if (!(cin >> choose))   // avoid looping forever if problem on cin
            break;
        cout << "selected: " << choose << endl;
        if (choose == 9)
            break;  
    }
return EXIT_SUCCESS;
}

Upvotes: 1

Related Questions