在使用getline处理重定向文件输入后,如何使用cin进行键盘输入

4

我知道这个问题已经被讨论很多次了,但是无论如何我都不能让它工作。这里我贴上一些代码:

#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) {
            ...
        }
    }
}

我已经尝试过插入一些cin.sync()、cin.clear()、cin.ignore()等,但什么都没有改变!


2
如果你的输入与整数格式不符合,那么该文本不会从输入缓冲区中删除,因此在下一次循环中将再次读取相同的输入。你需要 同时 忽略行尾其余部分 并且 清除错误。参见此处 std::istream::ignore 中的示例以了解如何实现。 - Some programmer dude
好的,我来试试!无论如何,输入都是一个文本文件(在Linux上,我使用控制台将文件流入程序"./myprogram < text"),这里是一个输入的例子: parent child1 child2 \ n parent1 child1 child2 child3 \ n ... - Moriduri
我刚刚尝试了以下代码:while (true) { cin.clear(); cin.ignore(std::numeric_limitsstd::streamsize::max(), '\n'); cin >> choose; switch (choose) { ... 等等... 但是没有任何变化 :( - Moriduri
我无法重现你的问题!你如何跳出第一个循环? - Christophe
在文本文件末尾添加零,然后在循环内部有一个if语句来检查line是否等于0,如果是,则跳出循环。 - Moriduri
显示剩余2条评论
1个回答

1

如果您不以某种方式打破它,第一个getline()循环将永远循环。如果以清洁的方式完成,这不应该是任何问题。实际上,我无法重现您的错误。

如何分析错误

因此,原因可能是cin状态不良或仍处于挂起状态的非数字非空格输入。为了帮助您找出原因,建议添加一些诊断代码:

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;
}

如果您的流状态不干净,尽管使用了 cin.clean(),那么这是因为您关闭了流或在控制台上输入了文件结束代码(Ctrl+DCtrl+Z 取决于系统)。
如果流状态干净但 peeked 字符不是数字(即十进制代码不在 48 到 57 之间),则是错误的输入将流设置为失败状态。
编辑:您的特定情况
查看您提供的诊断信息(fail=1,eof=1),似乎在第一个循环后,您已经到达了输入的结尾。因此,您的输入失败,因为没有更多数据可读取。您在 pastebin 上的输入文件确认了最后一行是您用来退出第一个循环的“0”。

根据我们的交流,我了解到您实际上已经从命令行中重定向了输入(例如 yourprogramme <yourinput.txt),并希望您的代码在重定向的文件输入达到其结尾时切换回键盘输入。不幸的是,这不是它的工作方式。如果你从文件中重定向输入,cin 将始终指向重定向的文件。

为了能够混合使用文件和键盘输入,您需要使用<fstream>,更具体地说是使用ifstream来从文件中读取,并保留cin用于键盘输入。

以下是稍微修改过的代码,它从命令行中获取文件名,但没有重定向(例如 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;
}

现在的输出结果为(使用 cout 和 cin.fail cin.bad ...): Cin 的状态:failed=1 bad=0 eof=1 下一个字符的十进制是:-1 - Moriduri
PS:这是我目前正在使用的输入文件的链接:http://pastebin.com/mXE8BxUn ;) - Moriduri
@Moriduri 好的:你文件的最后一行是“0”,这将会打破你的第一个循环。但是在此之后没有任何行:任何尝试在第二个循环中读取都将失败。尝试添加一个菜单选项到你的文件中,它就可以工作了! - Christophe
1
@Moriduri P.S:根据你的代码和提到的文件,我理解你是通过操作系统级别的输入重定向来运行程序(即 myprog <testfile.txt)。如果你这样做,在文件结尾时,程序将不会切换回键盘:重定向是确定的,因此当你到达文件结尾时,任何cin都会失败。如果你想混合使用文件输入和键盘输入,必须使用 ifstream 显式地读取文件并保留 cin 用于实际的键盘输入。 - Christophe
1
@Moriduri 我已经进行了编辑,详细向您展示如何完成此操作。 - Christophe
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接