getline() behaving weirdly *the first time*

Hi. I'm new to C and C++ (coming from PHP), and I'm currently just playing with it.

Below is a sample console program I wrote that promts the user to enter the names and phones of 10 to 50 people and then outputs them.
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
#include "stdafx.h"
#include <time.h>
#include <iostream>
#include <string>
using namespace std;

void wait(int seconds)
{
	clock_t endwait = clock() + seconds * CLOCKS_PER_SEC;
	while (clock() < endwait);
}

struct client {
	string phone;
	string name;
};

int main()
{
	try {
		cout << "How many clients would you like to enter? (must be between 10 and 50)" << endl;
		int input;
		cin >> input;
		if (!(10 <= input && input <= 50)) {
			throw exception("You may only enter between 10 and 50 clients.");
		}else {
			cout << "You're about to enter " << input << " clients." << endl;
			client * clients;
			clients = new client[input];
			for(int i=1; i<=input; i++) {
				cout << "Enter the name of client number " << i << ": ";
				getline(cin,clients[i - 1].name);
				cout << "Enter the phone of client number " << i << ": ";
				getline(cin,clients[i - 1].phone);
			}

			for(int i=0; i< input; i++) {
				cout << "Client number " << i + 1 << "'s phone number is " << clients[i].phone.data() << " and his/her name is " << clients[i].name.data() << endl;
			}
		}
	}
	catch(exception& e) {
		cout << "Error: " << e.what();
	}
	catch(...) {
		cout << "Unknown error...";
	}
	wait(60);
	return 0;
}


The problem is very odd. So odd I'm thinking it may be a problem with the compiler (Microsoft Visual Studio 2005 Professional).

The program compiles and executes successfully. It displays the data being inputted fine. But there is no way to enter the name of the first client. What I have on the screen is
Enter the name of client number 1: Enter the phone of client number 1:

Once I enter the number of client number 1, I go on and enter the rest of the data (both names and numbers) successfully.

Any ideas as to why this may happen? I mean, why on earth would it happen only with the first entry? Is there something wrong in my logic or something?

I tried various combinations of "<" and "<=" to make sure it's not something with the loop, but it all appears fine.

If I'm using a wrong approach, would you suggest a better one (I'd like to keep using the string class though)?
closed account (z05DSL3A)
The problem is very common, when you call cin >> input; (input being an int) it leaves any non numeric data in the stream, ie the return character the use press to enter the data. This is then picked up be your next call on the stream as an empty string.

To resolve the issue, clean up the stream after calling cin >> input; by calling something like cin.ignore(256,'\n');.

http://www.cplusplus.com/reference/
Last edited on
@Grey Wolf
Didn't had an idea it's like that. I always though everything up to the newline is ignored automatically. Guess this applies only to getline() (well... not really "ignoring" there though I guess - just capturing up to that point).

That solved my problem, thanks. But I did a little stress test to see if it's a flawless solution (to use as a rule of thumb in the future, you know), and it seems that if a write a number and a bogus input that's larger than 256 characters, I still get the same promt as before.

I guess if I can detect the position of the first newline and use that as the first argument of cin.ignore(), that will do it, but... how do I do THAT in C++? In PHP, I have strpos(), but I looked at the reference and searched the whole site for that and didn't found an equivalent type of function.

@landonmkelsey
I like your style. I'm afraid I'm still far away from achieving this type of tidyness though. I mean, I barely understand 1/3 of your code. I've never worked with maps, vectors and the like, but I think I see where this is going (reminds me of PHP's non fixed sized arrays), and I like it. Thanks for the idea.
Last edited on
closed account (z05DSL3A)
That solved my problem, thanks. But I did a little stress test to see if it's a flawless solution (to use as a rule of thumb in the future, you know), and it seems that if a write a number and a bug input that's larger than 256 characters, I still get the same promt as before.


I should probably have said use cin.ignore( numeric_limits<streamsize>::max(), '\n' ); remembering to #include <limits> but the point is you should be aware of what is left in the stream.
I should probably have said use cin.ignore( numeric_limits<streamsize>::max(), '\n' ); remembering to #include <limits> but the point is you should be aware of what is left in the stream.

That worked like a charm. Thanks a million. Now there's yet another type of syntax I'll be reading up on (templates I see... ok). Gee... wonders with C and C like languages never cease.
if (!(10 <= input && input <= 50)) {
The && (and) operator should be an || (or) operator. ;)
@QWERTYman
I don't think so. Changing it to || practically allows any numeric value. I tried entering "1" and "51" as values, and they both worked fine... they shouldn't.

OK, I have a new stupid problem... I'm trying to restart the program on errors, but the weirdest thing happens when I enter a letter in the first promt - the program is stuck in an infinite erronous loop. My new code is:
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
#include "stdafx.h"
#include <iostream>
#include <string>
#include <limits>
using namespace std;

int streamLimit = numeric_limits<streamsize>::max();

struct client {
	string phone;
	string name;
};

int main()
{
	while(true) {
		try {
			cout << "How many clients would you like to enter? (must be between 10 and 50)" << endl;
			int input;
			cin >> input;
			cin.ignore(streamLimit, '\n');
			if (!(10 <= input || input <= 50)) {
				throw exception("You may only enter between 10 and 50 clients.");
			}else {
				cout << "You're about to enter " << input << " clients." << endl << endl;
				client * clients;
				clients = new client[input];
				for(int i=0; i<input; i++) {
					cout << "Client number " << i + 1 << endl;
					cout << "Name: ";
					getline(cin, clients[i].name);
					cout << "Phone: ";
					getline(cin, clients[i].phone);
					cout << endl;
				}

				for(int i=0; i<input; i++) {
					cout << "Client number " << i + 1 << "'s phone number is " << clients[i].phone.data() << " and his/her name is " << clients[i].name.data() << endl;
				}
				system("PAUSE");/*TODO: Remove when done.*/
				return 0;
			}
		}
		catch(exception& e) {
			cout << "Error: " << e.what() << endl;
		}
		catch(...) {
			cout << "Unknown error..." << endl;
		}
		cout << "The program will restart..." << endl << endl;
		cin.clear();
	}
}


If I type any number at the promt "How many clients would you like to enter? (must be between 10 and 50)", it works fine, but if I enter for example "a", the program starts to constantly print
"Error: You may only enter between 10 and 50 clients.
The program will restart..."
never allowing me to enter any new value. I added the "cin.clear()" call hoping a "corrupted" input stream is the reason, but the problem still persists. What is causing this and how to eliminate it while still restarting the program on errors?
Last edited on
Whoops. Just noticed.
There may be a better way to do it for you but I'm pretty sure you could make input a string. Convert the string to an int (You'll have to look that up cuz I'm not really good with it). The I'm pretty sure any test on the int will return false if it wasn't an int to begin with.
At times like this, I wish C++'s conversion rules were as loose as PHP's... but they're not, so string to int convertion isn't trivial.

Directly fetching a string and comparing it's .data() doesn't work (i.e. it doesn't compile at all), and if I explicitly convert the .data() into an int, I seem to always get 1568924 as a result, which obviously always throws an exception.

The actual code I've adjusted at the top:
1
2
3
4
5
6
7
8
			string inputString;
			cin >> inputString;
			//cin.ignore(streamLimit, '\n');
			int input = (int) inputString.data();
			cout << inputString << ";" << input; /* Placed for debugging purposes */
			if (!(10 <= input && input <= 50)) {
				throw exception("You may only enter between 10 and 50 clients.");
			}

(the rest is as above)

The string being returned is correct, but when converted to int, it's always the same, regardless of the actual string.

I too think there may be a better way than converting a string into a number... there must be. But how?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <sstream>

int main()
{
    using namespace std;

    string s = "ZZZ";
    stringstream ss(s); // Could of course also have done ss("ZZZ") directly.
  
    int i;
  
    if( (ss >> i).fail() )
    {
        cout << "conversion failed" << endl;
    }else{
        cout << i << endl;
    }
    system("pause");
    return 0;
}

I can't really explain this but I think it'll do the job.
Last edited on
Well, it looks ugly, but it works. There's one small problem left though. If I type a string containg a space, the program gets restarted two times before I can do anything.

Is there a way to treat each line as a single input, while still detecting failure (i.e. restart the program just once)? I tried to use setf() to add a skipws flag in a hope that all spaces will be reduced, thus treating "a d" (for example) as "ad", thus restarting once. I'm either not setting this flag correctly, or I'm missing something else.

Here's the modified part now:
1
2
3
4
5
6
7
8
9
10
11
12
13
			cout << "How many clients would you like to enter? (must be between 10 and 50)" << endl;
			int input;
			string inputString;
			cin >> inputString;
			stringstream ss(inputString);
			ss.setf(ios_base::skipws);
			if((ss >> input).fail()) {
				throw exception("You must enter digits.");
			}
			//cin.ignore(streamLimit, '\n');
			if (!(10 <= input && input <= 50)) {
				throw exception("You may only enter between 10 and 50 clients.");
			}


Any ideas?
Topic archived. No new replies allowed.