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

5 回答 5

3

您必须cin.ignore()在选择选项后立即添加:

//get the input from the user
cin >> option;
cin.ignore();

并且cin.ignore()在您之后没有必要getline

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

问题出在options- 如果你没有cin.ignore()在它之后调用,选项将包含行尾并且循环将继续......

我希望这有帮助。

于 2011-02-02T17:05:38.253 回答
2

不要这样做:

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

在您尝试阅读 EOF 之前,不会设置 EOF 标志。最后一整行读取读取到(不超过)EOF。因此,如果您的输入为零,则 myfile.good() 为 true,并且循环已进入。然后,您尝试读取一行,它会失败,但您仍然会进行推送。

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

    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() 行,一切都会奏效。

于 2011-02-02T17:05:59.553 回答
1

您可以考虑使用 getline 然后尝试从中获取您的选项,而不是直接在您的流上使用“>>”。是的,它的“效率”较低,但在这种情况下效率通常不是问题。

你看,问题是用户可以在这里输入一些愚蠢的东西。例如,他们可以输入“二”之类的内容,然后按回车键,然后您的程序将进行调整,因为它愉快地继续一遍又一遍地尝试破译一个空选项。用户设置它的唯一方式(以及那些推荐使用的方式ignore())就是杀死你的程序。一个表现良好的程序不会以这种方式响应错误的输入。

因此,您最好的选择不是编写可能因最适度的用户无知/故障而严重崩溃的脆弱代码,而是编写可以优雅地处理错误情况的代码。你不能通过希望用户输入一个数字然后换行来做到这一点。总有一天,你会下注很差。

因此,您有两种选择来阅读您的选择。首先,从用户那里读取一整行,确保流仍然是好的,然后将你得到的字符串变成一个流并尝试从中读取你的整数,确保这个另一个流仍然是好的。第二个选项,尝试读取一个数字,验证流是否仍然良好,读取一行并确保流仍然良好并且您的字符串为空(或者如果不是,则忽略它,您的选择)。

于 2011-02-02T17:30:37.583 回答
1

@Vladimir 是对的。以下是该错误背后的机制:

当您输入选项“3”时,您实际放入流中的是“3\n”。cin >> option消耗“3”并留下“\n”。getline()消耗 "\n" 并且您对ignore()after的调用getline()等待用户输入。

如您所见,事件的顺序已经不是您所期望的。

现在,当 ignore() 等待输入时,您输入您的行。您正在输入的那一行将转到“cin >> 选项。

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

要做的事情:

  • 始终检查 cin.eof()、cin.fail() 和 cin.bad()。
  • 始终初始化您的变量并在可能的最窄范围内声明它们(在读取之前声明 option=0)。
于 2011-02-02T17:43:48.480 回答
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];
            }
于 2011-02-02T17:19:24.880 回答