Mixed data typeinput in C++
Why does this program run fine?
#include<iostream>
using namespace std;
int main()
{
cout <<"What year was your house built?\n";
int yea开发者_开发问答r;
cin >> year;
cout << "What is its street address?\n";
char address[80];
cin>>address;
cout << "Year built: " << year << endl;
cout << "Address: " << address << endl;
cout << "Done!\n";
return 0;
}
And why does this program not give the chance to enter the address?
#include <iostream>
int main()
{
using namespace std;
cout <<"What year was your house built?\n";
int year;
cin >> year;
cout << "What is its street address?\n";
char address[80];
cin.getline(address, 80);
cout << "Year built: " << year << endl;
cout << "Address: " << address << endl;
cout << "Done!\n";
return 0;
}
cin>>
leaves the newline character (\n) in the iostream. If getline
is used after cin>>
, the getline
sees this newline character as leading whitespace, thinks it is finished and stops reading any further.
Two ways to solve the problem:
Avoid putting getline
after cin >>
OR
Consume the trailing newline character from the cin>> before calling getline, by "grabbing" it and putting it into a "dummy" variable.
string dummy;
getline(cin, dummy);
Why does the first program work & Second doesn't?
First Program:
The cin
statement uses the entered year and leaves the \n
in the stream as garbage. The cin
statement does NOT read (or "grab") \n
. The cin
ignores \n
when reading data. So cin
in program 1 can read the data properly.
Second Program:
The getline
, reads and grabs \n
. So, when it sees the \n left out from cin
, it grabs the \n
and thinks it is finished reading, resulting in second program not working as you expected.
Sit down for a second. This is not easy to explain properly.
When your program gets to a point where it reads from std::cin, it does not just automatically wait for you to type something. std::cin is an input stream, the same as you use to read from a file on disk. The only reason it waits is if there is not enough data available yet to satisfy the read request.
Meanwhile, when you run your program from the console, the console window itself is also a program. It is interpreting your key presses and translating them into text, and feeding that text a line at a time to the standard input of your program (so that std::cin can see it). This is important and useful, because it allows the backspace key to work the way you expect it to.
So if your program is supposed to read a number, and you type a number, your program will not see the number until you hit return to complete the line. However, the newline character is still sitting in the input stream, because you didn't read it yet. The operator>>
skips whitespace before the value that it's trying to read, but it leaves behind any whitespace after the value.
Now, if the next reading operation is another call to operator>>
, then it does the same thing again and it works fine: the newline that we didn't read before is whitespace, so it gets skipped, and then the next thing gets read.
However, the getline()
function reads from the current point until the next newline. It never skips any leading or trailing whitespace, and an empty line is considered completely valid. So if you typed a number and hit return, then the getline()
call will see the newline and finish reading right away, because it already has an end of the line. The program does not stop because there was already enough data available to finish the operation.
To fix this, the safest, simplest and most robust way of dealing with the input is to always read the entire line first, and then re-interpret the contents of the line. To make this easier, we will use the std::string
class to represent strings. We can read into the string instance with std::getline
(notice: a global function, not a member function of cin
), and create a std::stringstream
instance from that string.
The idea is: the program will always wait at an input request, because the previous request always read the newline character (because we read the entire line). So that makes the control flow work the way we expected it to. The std::stringstream
instance can be treated like a file or standard input: it's just another stream, except it takes its data from the string. So we can get numbers out of it with operator>>
, and so on.
The other benefit of this comes when the user inputs invalid data. It can be quite hard to recover from this properly, if you are just reading directly from std::cin
. But if you are using the stringstream
as a "buffer", then you can just throw it away and try again with a new line of input.
An example:
#include <iostream>
#include <string>
#include <sstream>
int main()
{
using namespace std;
string line;
int year;
while (true) {
cout << "What year was your house built?" << endl;
getline(cin, line);
stringstream input(line);
if (line >> year) { break; }
}
cout << "What is its street address?\n";
getline(cin, line);
cout << "Year built: " << year << endl;
cout << "Address: " << line << endl;
cout << "Done!\n";
}
Maybe you have a stray terminator in cin?|
Try cin.clear();
精彩评论