newbie problem with cin and getline

D

Devendra_Vidhale

Hello,
I and new to C++ and tried to do a simple program using getline
library. The code is as follows-

#include<iostream>
using std::endl;
using std::cout;
using std::cin;

#include<string>
using std::string;
using std::getline;

int main() {
int number;
string name;

cout<<"Please enter your roll number: ";
cin>>number;

cout<<endl<<"Please enter your name: \n";
getline(cin, name);

cout<<"\nYou are "<<number<<". "<<name;
return 0;
} //end main

The code above compiles with GCC 4.2.1 (prerelease) on linux. The
problem is that the prompt "Please enter your name: " does not work
during execution. The program jumps directly from first cout statement
to third one, and exits. The string value stays blank.
However, if I comment out the lines for variable number, and remove
references from everywhere, the program prompts for the string
properly.
How should I receive the string from user after taking the int ? Is
there some way to flush the cin stream, like cout<<endl does for cout
stream ? Or is there some thing else I am missing ?

Devendra
 
K

Kai-Uwe Bux

Devendra_Vidhale said:
Hello,
I and new to C++ and tried to do a simple program using getline
library. The code is as follows-

#include<iostream>
using std::endl;
using std::cout;
using std::cin;

#include<string>
using std::string;
using std::getline;

int main() {
int number;
string name;

cout<<"Please enter your roll number: ";
cin>>number;

cout<<endl<<"Please enter your name: \n";
getline(cin, name);

cout<<"\nYou are "<<number<<". "<<name;
return 0;
} //end main

The code above compiles with GCC 4.2.1 (prerelease) on linux. The
problem is that the prompt "Please enter your name: " does not work
during execution. The program jumps directly from first cout statement
to third one, and exits. The string value stays blank.
However, if I comment out the lines for variable number, and remove
references from everywhere, the program prompts for the string
properly.
How should I receive the string from user after taking the int ? Is
there some way to flush the cin stream, like cout<<endl does for cout
stream ? Or is there some thing else I am missing ?

If, at the first prompt, you enter the line

12675681 given_name family_name

and then press enter, you will find that the names are read. What happens if
you press enter after the roll number is that the end-of-line character
remains (since it is not part of the number) and then causes getline() to
see an empty string. Try reading that intermediate end-of-line character
before you call getline() to get the name.


Best

Kai-Uwe Bux
 
A

arnuld

#include<iostream>
using std::endl;
using std::cout;
using std::cin;

#include<string>
using std::string;
using std::getline;

int main() {
int number;
string name;

cout<<"Please enter your roll number: ";
cin>>number;

cout<<endl<<"Please enter your name: \n";
getline(cin, name);

cout<<"\nYou are "<<number<<". "<<name;
return 0;
} //end main

The code above compiles with GCC 4.2.1 (prerelease) on linux. The
problem is that the prompt "Please enter your name: " does not work
during execution. The program jumps directly from first cout statement
to third one, and exits. The string value stays blank.
However, if I comment out the lines for variable number, and remove
references from everywhere, the program prompts for the string
properly.
How should I receive the string from user after taking the int ? Is
there some way to flush the cin stream, like cout<<endl does for cout
stream ? Or is there some thing else I am missing ?

Devendra

perhaps you want this:

#include <iostream>
#include <string>

int main()
{
std::cout << "Enter your roll number: ";
int student_roll_number;
std::cin >> student_roll_number;

std::cout << "Enter your name: ";
std::string student_name;
std::cin >> student_name;

std::cout << "you are :: "
<< student_name
<< " - "
<< student_roll_number
<< std::endl;

return 0;
}


i think it is clearer than your code :). here is the output:

~/programming/cpp $ g++ -ansi -pedantic -Wall -Wextra test.cpp
~/programming/cpp $ ./a.out
Enter your roll number: 1251
Enter your name: arnuld
you are :: arnuld - 1251
~/programming/cpp $ g++ --version
g++ (GCC) 4.2.1 20070704 (prerelease)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

~/programming/cpp $
 
L

Lionel B

If, at the first prompt, you enter the line

12675681 given_name family_name

and then press enter, you will find that the names are read. What
happens if you press enter after the roll number is that the end-of-line
character remains (since it is not part of the number) and then causes
getline() to see an empty string. Try reading that intermediate
end-of-line character before you call getline() to get the name.

cin.get();

after the cin>>number will do that. However, if there are any non-numeric
characters (such as spaces) after the number entered, the problem recurs
(ok, so it's user error, but...).

Maybe a more robust solution is:

getline(cin, garbage);

after the cin>>number, to eat any trailing garbage...

Regards,
 
A

arnuld

The solution above doesn't allow to enter names containing spaces.


yes, i did not took space in to consideration nd now i knew that the
std::string expects only a word and nothing else. so what wil you do to
resolve this issue.

use "getline" ? or we can fix that issue in "std::string" ??


-- http://arnuld.blogspot.com
 
B

BobR

Lionel B said:
cin.get();

after the cin>>number will do that. However, if there are any non-numeric
characters (such as spaces) after the number entered, the problem recurs
(ok, so it's user error, but...).

Maybe a more robust solution is:

getline(cin, garbage);

after the cin>>number, to eat any trailing garbage...

Why not just: (#include <limits>)
cin.ignore( std::numeric_limits<int>::max() );

For the OPs problem, the following will probably do it:

int main(){
int number;
string name;
cout<<"Please enter your roll number: ";
cin>>number;

cin.ignore(); // skip one char
// or:
// cin.ignore( 20 ); // skip 20 chars

cout<<endl<<"Please enter your name: \n";
getline( cin, name );
cout<<"\nYou are "<<number<<". "<<name;
return 0;
} // end main
 
D

Devendra_Vidhale

arnuld said:
perhaps you want this:

#include <iostream>
#include <string>

int main()
{
std::cout << "Enter your roll number: ";
int student_roll_number;
std::cin >> student_roll_number;

std::cout << "Enter your name: ";
std::string student_name;
std::cin >> student_name;

std::cout << "you are :: "
<< student_name
<< " - "
<< student_roll_number
<< std::endl;

return 0;
}


i think it is clearer than your code :). here is the output:

~/programming/cpp $ g++ -ansi -pedantic -Wall -Wextra test.cpp
~/programming/cpp $ ./a.out
Enter your roll number: 1251
Enter your name: arnuld
you are :: arnuld - 1251
~/programming/cpp $ g++ --version
g++ (GCC) 4.2.1 20070704 (prerelease)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

~/programming/cpp $

Arnuld,
Can you please point out the difference between your code and mine ? I
don't see anything obvious. How does your code take care of the
newline character in cin stream ?

I got following reply from Kai-Uwe Bux, which explains the issue with
newline character -
If, at the first prompt, you enter the line
12675681 given_name family_name
and then press enter, you will find that the names are read. What happens if
you press enter after the roll number is that the end-of-line character
remains (since it is not part of the number) and then causes getline() to
see an empty string. Try reading that intermediate end-of-line character
before you call getline() to get the name.

Kai-Uwe Bux

Thanks,
Devendra
 
A

arnuld

cin.get();

this is much better than these 2:

cin.ignore(std::numeric_limits<int>::max()) cin.ignore( 20 )

and i found that both "cin.ignore()" & "cin.get()" are similar in use. Now
if user presss enter more than 1 time then that should belong to the user
as user-error.
after the cin>>number will do that. However, if there are any
non-numeric characters (such as spaces) after the number entered, the
problem recurs (ok, so it's user error, but...).

Maybe a more robust solution is:

getline(cin, garbage);

error: garbage was not declared in this scope

(or do i need some #include ???")
 
A

arnuld

Arnuld,
Can you please point out the difference between your code and mine ? I
don't see anything obvious. How does your code take care of the newline
character in cin stream ?

my code uses "std::string" and it discards any leading and trailing
whitespaces. so the programme will work correctly no matter how many
"newlines" or other whitespace characters user inputs in-between roll
number and name but that will give you the 1st name only. even if yuo
enter the full name, you will get 1st name only. i think this is not what
you want :-(

I got following reply from Kai-Uwe Bux, which explains the issue with
newline character -


Kai-Uwe Bux, is right. i think "std::cin.get()" or "std::cin.ignore()"
will solve your problem. i will wait for more replies to get more insights
:)
 
L

Lionel B

Why not just: (#include <limits>)
cin.ignore( std::numeric_limits<int>::max() );

For the OPs problem, the following will probably do it:

int main(){
int number;
string name;
cout<<"Please enter your roll number: "; cin>>number;

cin.ignore(); // skip one char
// or:
// cin.ignore( 20 ); // skip 20 chars

cout<<endl<<"Please enter your name: \n"; getline( cin, name );
cout<<"\nYou are "<<number<<". "<<name; return 0;
} // end main

Neither form seems terribly satisfactory (did you try them?).

With cin.ignore() any trailing stuff (including spaces) screws things up.

With cin.ignore( 20 ) I can't even figure out how to get prompted for the
name at all!
 
L

Lionel B

this is much better than these 2:

cin.ignore(std::numeric_limits<int>::max()) cin.ignore( 20 )

and i found that both "cin.ignore()" & "cin.get()" are similar in use.
Now if user presss enter more than 1 time then that should belong to the
user as user-error.


error: garbage was not declared in this scope

Well, declare it then :)

string garbage;
(or do i need some #include ???")

No.
 
L

Lionel B

my code uses "std::string" and it discards any leading and trailing
whitespaces. so the programme will work correctly no matter how many
"newlines" or other whitespace characters user inputs in-between roll
number and name but that will give you the 1st name only. even if yuo
enter the full name, you will get 1st name only. i think this is not
what you want :-(




Kai-Uwe Bux, is right. i think "std::cin.get()" or "std::cin.ignore()"
will solve your problem. i will wait for more replies to get more
insights :)

I pointed out some problems with those. I urge you to try:

#include<iostream>
using std::endl;
using std::cout;
using std::cin;

#include<string>
using std::string;
using std::getline;

int main() {
int number;
string name;

cout<<"Please enter your roll number: ";
cin>>number;

string garbage;
getline(cin, garbage);

cout<<endl<<"Please enter your name: \n";
getline(cin, name);

cout<<"\nYou are "<<number<<". "<<name;
return 0;
} //end main
 
R

Robert Bauck Hamar

arnuld said:
my code uses "std::string" and it discards any leading and trailing
whitespaces. so the programme will work correctly no matter how many
"newlines" or other whitespace characters user inputs in-between roll
number and name but that will give you the 1st name only. even if yuo
enter the full name, you will get 1st name only. i think this is not what
you want :-(

Both examples uses std::string. The difference is how they are input. Assume
at the point where you are asked about the number, you enter the following

42

That is, you enter the characters '4', '2', and '\n', which are given to
cin. This is, however not really a case with C++. Your terminal/console
does this: Whenever your programme needs more input, it asks the
terminal/console for input, and this gives the user the chance of typing in
one line of input.

At this point, cin has been requested to read an integer. To accomplish
this, cin finds the numeric characters '4' and '2', and the next
character, '\n', is not a number, so cin converts '4' and '2' to 42, and
saves the rest of its input, '\n', for later use.

In the original programme, the next request for cin is a getline. Getline
reads from cin's input until it finds a '\n'. Since cin has saved
characters from the last input, it doesn't need to ask the user. Getline
reads the '\n' and calls that a line.

Arnuld's programme does something different. cin >> string tries to input a
word, that is it finds the next sequence of non-space characters (a.k.a.
whitespace) and reads these. Using this, cin skips the '\n', because '\n'
is a space character. Now, cin has no more input to use, so it asks the
terminal for more, and the terminal will give you the chance to input
another line. But, the same thing happens as before. If you enter

Art Dent

Cin will see 'Art' as a word, and save ' ', 'D', 'e', 'n', 't' and '\n' for
the next input.

The solution to this is: Whenever your programme expects one line of input,
read the whole line, nothing more, nothing less. Sometimes the best
solution is to use getline all the time, and parse this line, other times,
you only need to discard the rest of the line after input. To discard
input, you can use:

std::string discard;
getline(std::cin, discard);

or

std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

Since you probably will discard a lot, the ignore() solution will become
less verbose if you save the max value:

const std::streamsize infinity =
std::numeric_limits<std::streamsize>::max();
 
K

Kai-Uwe Bux

Lionel said:
cin.get();

after the cin>>number will do that. However, if there are any non-numeric
characters (such as spaces) after the number entered, the problem recurs
(ok, so it's user error, but...).

Maybe a more robust solution is:

getline(cin, garbage);

after the cin>>number, to eat any trailing garbage...

Or, one could make it an error if there is no \n:


#include<iostream>
using std::endl;
using std::cout;
using std::cin;

#include<string>
using std::string;
using std::getline;

#include <istream>

class expect {
// based on an idea of Dietmar Kuehl
private:

char const * char_ptr;
char single [2];

friend
std::istream & operator>> ( std::istream & istr,
expect const & );
public:

expect ( char const * c_str )
: char_ptr ( c_str )
{}

expect ( char const & chr )
: char_ptr ( single )
{
single[0] = chr;
single[1] = 0;
}

}; // expect

std::istream & operator>> ( std::istream & istr,
expect const & the_string ) {
char chr;
char const * ptr = the_string.char_ptr;
while ( ( *ptr != 0 )
and
( istr.get( chr ) )
and
( chr == *ptr )
) {
++ ptr;
}
if ( *ptr != 0 ) {
istr.setstate( std::ios_base::failbit );
}
return( istr );
}



int main() {
int number;
string name;
cout<<"Please enter your roll number: ";
if ( cin>> number >> expect("\n") ) {
cout<<endl<<"Please enter your name: ";
getline( cin, name );
cout << "\nYou are " <<number << ". " << name << '\n';
} else {
cout << "You messed up.\n";
}
} //end main


Best

Kai-Uwe Bux
 
A

arnuld

I pointed out some problems with those. I urge you to try:

#include<iostream>
using std::endl;
using std::cout;
using std::cin;

#include<string>
using std::string;
using std::getline;

int main() {
int number;
string name;

cout<<"Please enter your roll number: "; cin>>number;

string garbage;
getline(cin, garbage);

cout<<endl<<"Please enter your name: \n"; getline(cin, name);

cout<<"\nYou are "<<number<<". "<<name; return 0;

Lionel, this programme gives me this output:

[arnuld@arch cpp ]% ./a.out
Please enter your roll number: 1231

Please enter your name:
arnuld

You are 1231. arnuld%
[arnuld@arch cpp ]%


did you notice the "%" at the end of output. i just added "std::endl" in
the last line and that "%" disappeared :) but it took me 15 minutes to
think and find-out why i am getting "%" in the output, then some thing
just clicked - "flush the buffer" :)
 
G

Guest

I pointed out some problems with those. I urge you to try:

#include<iostream>
using std::endl;
using std::cout;
using std::cin;

#include<string>
using std::string;
using std::getline;

int main() {
int number;
string name;

cout<<"Please enter your roll number: "; cin>>number;

string garbage;
getline(cin, garbage);

cout<<endl<<"Please enter your name: \n"; getline(cin, name);

cout<<"\nYou are "<<number<<". "<<name; return 0;

Lionel, this programme gives me this output:

[arnuld@arch cpp ]% ./a.out
Please enter your roll number: 1231

Please enter your name:
arnuld

You are 1231. arnuld%
[arnuld@arch cpp ]%


did you notice the "%" at the end of output. i just added "std::endl" in
the last line and that "%" disappeared :) but it took me 15 minutes to
think and find-out why i am getting "%" in the output, then some thing
just clicked - "flush the buffer" :)

Actually not, I think (but I'm not 100% sure) that the buffers will be
flushed when the program terminates (might be a OS feature and thus not
guaranteed). The problem you had was that there was no newline output at
the end, the % you see is part of your prompt from whatever shell you
are running. Exchanging the endl, to a "\n" will do just as well.
 
B

BobR

Lionel B said:
Neither form seems terribly satisfactory (did you try them?).

With cin.ignore() any trailing stuff (including spaces) screws things up.

With cin.ignore( 20 ) I can't even figure out how to get prompted for the
name at all!

So sorry! I keep forgetting that 'std::cin' is NOT an istream, so can not
react like an istream normally would in that situation.

Why would you use std::cin to prompt for the name? I usually use std::cout
for that.
When I see "enter a number:", I usually don't enter "12345 Joe
Shmuck[enter]". How did you know to enter the name on the same line? Must be
some clever trick. Could you show me that code?

I think your best bet is to use 'stdin'.
 
A

Anand Hariharan

yes, i did not took space in to consideration nd now i knew that the
std::string expects only a word and nothing else. so what wil you do to
resolve this issue.

use "getline" ? or we can fix that issue in "std::string" ??

The issue is not with string (strings can have spaces in them) but
with cin and how it handles input.

- Anand
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top