在C++中使用getline忽略空格

3

嘿,我正在尝试编写一个程序,可以接受人们的新任务并将其添加到堆栈中,能够显示任务,能够将该堆栈保存到文本文件中,然后读取文本文件。问题出现在当我尝试从用户那里接收输入时,每当您输入带有空格的字符串时,选择要执行的操作的菜单就会循环。我需要一种解决方法。任何帮助都将不胜感激。

// basic file io operations
#include <iostream>
#include <fstream>
#include <stack>
#include <string>
using namespace std;

int main () {
    //Declare the stack
    stack<string> list;

    //Begin the loop for the menu
    string inputLine;
    cout << "Welcome to the to-do list!" << endl;

    //Trying to read the file
    ifstream myfile ("to-do.txt");
    if(myfile.is_open()){

        //read every line of the to-do list and add it to the stack
        while(myfile.good()){
            getline(myfile,inputLine);
            list.push(inputLine);
        }
        myfile.close();
        cout << "File read successfully!" << endl;
    } else {
        cout << "There was no file to load... creating a blank stack." << endl;
    }

    int option;

    //while we dont want to quit
    while(true){
        //display the options for the program
        cout << endl << "What would you like to do?" << endl;
        cout << "1. View the current tasks on the stack." << endl;
        cout << "2. Remove the top task in the stack." << endl;
        cout << "3. Add a new task to the stack." << endl;
        cout << "4. Save the current task to a file." << endl;
        cout << "5. Exit." << endl << endl;

        //get the input from the user
        cin >> option;

        //use the option to do the necessary task
        if(option < 6 && option > 0){
            if(option == 1){
                //create a buffer list to display all
                stack<string> buff = list;
                cout << endl;
                //print out the stack
                while(!buff.empty()){
                    cout << buff.top() << endl;
                    buff.pop();
                }
            }else if (option == 2){
                list.pop();
            }else if (option == 3){
                //make a string to hold the input
                string task;
                cout << endl << "Enter the task that you would like to add:" << endl;
                getline(cin, task); // THIS IS WHERE THE ISSUE COMES IN
                cin.ignore();

                //add the string
                list.push(task);
                cout << endl;
            }else if (option == 4){
                //write the stack to the file
                stack<string> buff = list;
                ofstream myfile ("to-do.txt");
                if (myfile.is_open()){
                    while(!buff.empty()){
                        myfile << buff.top();
                        buff.pop();
                        if(!buff.empty()){
                            myfile << endl;
                        }
                    }
                }
                myfile.close();
            }else{
                cout << "Thank you! And Goodbye!" << endl;
                break;
            }
        } else {
            cout << "Enter a proper number!" << endl;
        }
    }
}

cin >> option 之前,您可以尝试使用 cin.ignore() - Murilo Vasconcelos
3
您需要对所有输入操作执行错误检查(通过测试流,例如 if (!std::cin) { /* 处理错误 */ }),并且您的输入循环不正确:有关编写正确的输入循环的方法,请参见此答案 - James McNellis
2
仅为美观起见:switch/case/default 代码块比所有这些 if/else if/else 更易读... - Emmanuel
你指的“just loops”是什么意思?你能添加一个样本输出截图吗? - user3458
是的,Emmanuel,我要从C#转到C++,所以我不知道那些是如何工作的。我只是按照我所知道的去做。 - Cistoran
显示剩余5条评论
5个回答

3
您需要在选择选项后添加 cin.ignore()
//get the input from the user
cin >> option;
cin.ignore();

在使用getline后,cin.ignore()是不必要的:

    getline(cin, task); // THIS IS WHERE THE ISSUE COMES IN
        //cin.ignore();

问题在于options - 如果您在其后没有调用cin.ignore(),则选项将包含行尾符,循环将继续......希望这可以帮助您。

如果用户输入“2 \n”,你认为在这种情况下使用ignore()是正确的吗? - Edward Strange

2

不要这样做:

    while(myfile.good())
    {
        getline(myfile,inputLine);
        list.push(inputLine);
    }

只有在尝试读取超过EOF时,才会设置EOF标志。最后一行完整的读取直到(但不包括)EOF。因此,如果您没有剩余输入,则myfile.good()为true并进入循环。然后尝试读取一行,它将失败,但仍将进行push操作。

读取文件中所有行的标准方法是:

    while(getline(myfile,inputLine))
    {
        list.push(inputLine);
    }

只有当文件包含数据时,才进入循环。

你的另一个问题似乎源于你有:

 std::getline(std::cin,task); // THIS is OK
 std::cin.ignore();           // You are ignoring the next character the user inputs.
                              // This probably means the next command number.
                              // This means that the next read of a number will fail
                              // This means that std::cin will go into a bad state
                              // This means no more input is actually read.

所以,只需删除cin.ignore()这行代码,一切都会正常工作。

1

不要直接在流上使用">>",而是考虑使用getline,然后尝试从中获取您的选项。 是的,这样效率会降低,但效率通常在这种情况下并不是问题。

您看,问题在于用户可能会在此处输入一些愚蠢的内容。 例如,他们可以输入类似"two"的内容,然后按回车键,然后您的程序将继续尝试解析一个空选项,一遍又一遍地抛出异常。 您目前设置的唯一解决方法(以及那些建议使用ignore()的人建议的方法)是关闭您的程序。 一个表现良好的程序不会对错误输入做出这种反应。

因此,您最好的选择不是编写容易出现严重故障的脆弱代码,而是编写能够优雅地处理错误条件的代码。 您不能通过希望用户输入数字然后换行来实现这一点。 不可避免地,总有一天,您会赌错。

所以,你有两个选项来读取你的选项。 第一种方法是读取用户的完整行,确保流仍然良好,然后将你获得的字符串转换为流,并尝试从中读取你的整数,确保此其他流仍然良好。 第二个选择是尝试读取数字,验证流仍然良好,读取一行并确保流仍然良好,并且你的字符串为空(如果不为空,可以选择忽略它)。

1

@Vladimir 是对的。这就是错误背后的机制:

当选择选项“3”时,实际上你将“3\n”放入流中。 cin >> option 消耗了“3”,并留下了“\n”。 getline() 消耗了“\n”,在 getline() 后调用 ignore() 的情况下,它会等待用户输入。

正如您所看到的,事件序列已经不是您所期望的。

现在,在 ignore() 等待输入时,您要键入一行。你正在输入的那一行就是将进入 “cin >> option”的内容。

如果只给它一个符号,ignore() 将为你处理它,并且正确读取选项。但是,如果您给它非数字符号,当尝试读取选项时,流将设置 failbit。从那时起,您的流将拒绝执行任何操作。任何 << 或 getline 都不会在它们应该更改的变量中设置任何新值。你将保持选项中的 3 和任务中的 "",处于紧密循环中。

要做的事情:

  • 始终检查cin.eof()、cin.fail()和cin.bad()。
  • 始终初始化变量并在尽可能窄的范围内声明它们(在读取之前声明option=0)。

0

我刚找到了一种方式,可以通过它进行某种形式的黑客攻击,虽不是最好的方法,但它起作用。创建一个字符数组,然后在该数组中接受输入,最后将所有内容放入字符串中。

char buff[256];
            cout << endl << "Enter the task that you would like to add:" << endl;
            cin >> task;
            task += " ";
            cin.getline(buff, 256);
            for(int i = 1; buff[i] != 0; i++){
                task += buff[i];
            }

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