关于字符串:在C ++中使用getline忽略空格

Ignore Spaces Using getline in C++

本问题已经有最佳答案,请猛点这里访问。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// 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.ignore()

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

并且getline之后不需要cin.ignore()

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

问题出在options中-如果您未在其后调用cin.ignore(),则选项将包含行尾并且循环将继续...

我希望这有帮助。


@弗拉基米尔是对的。这是该错误的背后机制:

输入选项" 3"时,实际输入的内容是" 3 n"。 cin >> option使用" 3"并保留" n"。 getline()消耗" n",并且在getline()等待用户输入后您对ignore()的调用。

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

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

如果只给它一个符号,ignore()将为您处理它,并且选项将被正确读取。但是,如果为它提供非数字符号,则在尝试读取该选项时,流将设置故障位。从那时起,您的信息流将拒绝执行任何操作。任何<<或getline都不会在应该更改的变量中设置任何新值。您将紧密地选择3个任务和一个任务。

要做的事情:

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


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

您会发现,问题在于用户可能在此处输入一些愚蠢的内容。例如,他们可以输入诸如"两个"之类的内容,然后按Enter键,然后您的程序将逐渐适应,因为它很乐意继续尝试一遍又一遍地破译一个空选项。用户唯一的求助方式(以及建议使用ignore()的建议方式)是杀死您的程序。行为良好的程序不会以这种方式对错误的输入做出响应。

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

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


不要这样做:

1
2
3
4
5
    while(myfile.good())
    {
        getline(myfile,inputLine);
        list.push(inputLine);
    }

直到尝试读取EOF之后,才会设置EOF标志。最近的全行读取最多读取EOF(不超过位)。因此,如果您的输入为零,则myfile.good()为true,并且会陷入循环。然后,您尝试读取一行,但该行将失败,但是您仍然可以进行推送。

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

1
2
3
4
    while(getline(myfile,inputLine))
    {
        list.push(inputLine);
    }

这样,仅当文件包含数据时才进入循环。

您的另一个问题似乎源于以下事实:

1
2
3
4
5
6
 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
2
3
4
5
6
7
8
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];
            }