First getline getting ignored

Hi! I'm coding an small program where I need the user to send strings, so I'm using the geline(cin,x) function.

Now, I need the user to send as many strings as they need, but the first geline is getting ignored. This isn't the first time it happens to me and my classmates, but as far as I now no one has found the precise reason (we've used some "tricks" to solve it, but have never found the exact reason).

An example of this is on the following code (look directly at lines 56 and 61):

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
  #include <iostream>
#include <sstream>
using namespace std;
class ColeccionFrase{
private:
	int cantidad;
	int size;
	string frase[40];
public:
	ColeccionFrase(){
		cantidad=0;
		size=40;
		for (int i=0;i<size;i++){
			frase[i]="";
		}
	}
	ColeccionFrase(int n){
		cantidad=0;
		size=n;
		for (int i=0;i<size;i++){
			frase[i]="";
		}
	}
	~ColeccionFrase(){
	}
	void agregarElemento(string elem){
		if (cantidad <= size){
			frase[cantidad] = elem; 
			cantidad = cantidad + 1; 
		}
	}
	//To String
	string toString(){
		stringstream s;
		for(int i=0;i<cantidad;i++){
			s<<i+1<<") "<<frase[i]<<endl;
		}
		return s.str();
	}	
	//Get 
	string getfrase(int i){
		return frase[i];
	}
	//Set 
	void setfrase(int i, int n){
		frase[i]=n;
	}
};
int main(int argc, char *argv[]) {
	string frase;
	int n;
	int contador=1;
	
	cout<<"Ingrese la cantidad de frases a guardar: "; //<---Amount of strings to add
	cin>>n;
	getline(cin,frase); //<---- This line gets ignored, I'm using it so the ones that matter won't. Without this line the first iteration would be lost.
	
	ColeccionFrase FraseA;
	do{
		cout<<contador<<") ";
		getline(cin,frase);		
		FraseA.agregarElemento(frase);
		contador++;
	}while(contador<=n);
	cout<<endl<<FraseA.toString();
	return 0;
}


Does anyone know why this happens and how to solve it? I can still use this kind of "hot fixes", but I'm curious about finding a way to fix this.

Thanks!
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
const auto LineLimit = numeric_limits<streamsize>::max();

int n;
cin >> n; // reads an integer from the current position
          // leaving the rest of the line including the newline in the input buffer.

// you need to "ignore" the rest of the line (which will at least be a newline character)
// so that your getline doesn't just read the end of this line
cin.ignore(LineLimit, '\n'); // read and ignore up to and including a '\n'

string frase;
getline(cin, frase); // this should now read the next line 

The "magic" LineLimit value causes "ignore" to ignore an unlimited number of chars until it finds a '\n'. Any lower value gives a bound to the maximum number of chars to ignore.
Last edited on
An alternative to using std::ignore is modify std::cin in std::getline so the input stream ignore all leading white-space, including the carriage return.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <string>

int main()
{
   std::cout << "Pick 1 or 2: ";
   int choice { };
   std::cin >> choice;
   std::cout << '\n';

   std::cout << "Now enter your name: ";
   std::string name { };
   // std::ws ignores leading spaces
   std::getline(std::cin >> std::ws, name);
   std::cout << '\n';

   std::cout << "Hello, " << name << ", you picked " << choice << '\n';
}
Pick 1 or 2: 2

Now enter your name: Rufus Kelly

Hello, Rufus Kelly, you picked 2
Hello sarvcr,

getline(cin, frase); //<---- This line gets ignored . Not entirely correct. It is not ignoring the line it is just eating the (\n) left in the buffer so that the next "std::getline" will work.

What you must first understand is cin >> n; is called formatted input. First it detects the type of variable being used, in this case an "int", and excepts a whole number to be entered. If not "std::cin" will be put in a failed state and unusable the rest of the program. So if the 1st "std::getline()" is being ignored that would be because "std::cin" has failed and is unusable. The other part of formatted input is that it leaves the (\n) in the buffer for whatever is next.

What you type on the keyboard is not put directly into a variable. It 1st goes into a buffer until you press "Enter" then it will try to put what you have typed into the variable.

The other thing with formatted input is that it will stop at the first white space, that being (space, \t, \v, \f, \r and \n) as seen at http://www.cplusplus.com/reference/cctype/isspace/ there may be others I could not find the table I was looking for, or a (\n) whichever comes 1st. But when it stops at a (\n) it leaves this in the buffer and needs to be cleared before any "std::getline" is called.

Either of the above solutions will work. I tend to just use:
 
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>. 

When I first was shown this it was said to be the most portable way to write it as any compiler and set of header files can use it.

The other side is unformatted input, the "std::getline()". Once you press "Enter" "std::getline" will store what was typed on the keyboard including any type of white space. It also reads the (\n) and discards it as it is not needed for a "std::string". This leaves you with an empty buffer for whatever is next for input.

This also applies to reading from a file.

In addition to the above 2 examples you can use: std::cin.ignore();. This will ignore the default 1 character or until it reaches "Trates::eof": https://en.cppreference.com/w/cpp/io/basic_istream/ignore

The other option is: std::cin.ignore(1000, '\n'). It all comes down to what you will need to do.

I reworked your program and added some comments, see what you think:
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <iostream>
#include <limits>
#include <string>

#include <sstream>

using namespace std;

constexpr int MAXSIZE{ 40 };

class ColeccionFrase
{
    private:
        int cantidad{};
        int size{MAXSIZE};
        string frase[MAXSIZE];

    public:
        //ColeccionFrase(){}  // <--- Can be written as this.
        ColeccionFrase()
        {
            //cantidad = 0;

            //size = 40;

            //for (int i = 0; i < size; i++)
            //{
            //    frase = "";
            //}
        }

        //ColeccionFrase(int amtUsed) : size(amtUsed) {}
        ColeccionFrase(int amtUsed)
        {
            //cantidad = 0;
            size = amtUsed;

            //for (int i = 0; i < size; i++)
            //{
            //    frase[i] = "";
            //}
        }

        ~ColeccionFrase()  // <--- Not needed because the compiler will create this for you. OK if you leave.
        {
        }

        void agregarElemento(string elem)
        {
            if (cantidad <= size)  // <--- Should just be (<) as "size" could be 40 which is 1 past the end of the array.
            {
                frase[cantidad] = elem;

                cantidad = cantidad + 1;  // <--- "cantidad++;" is all you need.
            }
        }

        //To String
        string toString()
        {
            stringstream s;

            for (int i = 0; i < cantidad; i++)
            {
                s << i + 1 << ") " << frase[i] << '\n';
            }

            return s.str();
        }

        //Get 
        string getfrase(int i)
        {
            return frase[i];
        }

        //Set 
        void setfrase(int i, int n)
        {
            frase[i] = n;
        }
};

int main(int argc, char *argv[])  // <--- If you are not using "argc" and "argv" they are not needed.
{
    string frase;  // <--- Empty when defined. Does not need initialized.
    int amtUsed{};  // <--- [b][i]ALWAYS initialize all your variables.[/b]
    int contador{ 1 };

    cout << "Ingrese la cantidad de frases a guardar: "; //<---Amount of strings to add
    cin >> amtUsed;

    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.

    std::cout << '\n';

    //getline(cin, frase); //<---- This line gets ignored, I'm using it so the ones that matter won't. Without this line the first iteration would be lost.

    //ColeccionFrase FraseA;  // <--- Uses default ctor.
    ColeccionFrase fraseA(amtUsed);  // <--- Should be using this 1. Uses overloaded ctor.

    do
    {
        cout << contador << ") ";

        getline(cin, frase);

        fraseA.agregarElemento(frase);

        contador++;
    } while (contador <= amtUsed);

    cout << '\n' << fraseA.toString();
    
    return 0;  // <--- Not required, but makes a good break point for testing.
}

At the top I added some header files. DO NOT count on "sstream" including "string" this may not happen with all compilers. it is better to include what you need then if a header file includes the same header file the include guards will only let it compile once.

In your class you are doing more work in your ctors than you need to. The default ctor can be empty because you can initialize your variables when defined.

In the overloaded the only thing you need is to set the "size" variable.

The for loops in each ctor have no use. A "std::string" is empty with no size when defined and giving it a value of ("") has no effect. It is still empty with no size.

Since you have nothing special for your dtor the compiler will still provide a default dtor at compile time. Only when you define an overloaded ctor or dtor do you need to define a default ctor or dtor.

Your class function "toString()" works because it is part of the class, but keep in mind that the "string" class has a member function called "toString()" and your class function name is confusing.

I changed some of the variable names. Try to avoid using single letter variable names as they have no meaning and are hard to follow.

Using "i", "j" or "k" in a for loop is OK because these variables are local to the for loop and are destroyed when the loop ends.

Line 97 is just a replacement for line 93. Other than the ignore there ar simplier ways to eat the (\n) left in the buffer.

Including line 93 eliminates line 97.

See if any of this helps you out.

Andy
Topic archived. No new replies allowed.