Contents

Chapter 5

Input: Letting the Reader Talk Back

Reading what someone types, and the newline that ruins everything.

Every program you’ve written says the same thing every time you run it. Chapter 3 gave you variables, but you were the one filling them in. Let’s hand that job to whoever is running the program.

Asking for a word

#include <iostream>
#include <string>

int main() {
    std::cout << "What is your name? ";

    std::string name;
    std::cin >> name;

    std::cout << "Hello, " << name << "!\n";
}
What is your name? Ada
Hello, Ada!

std::cin is the other end of std::cout: the way in. And look at the arrows: they’ve turned around. With cout the text flows out of your program; with cin it flows out of the input and into your variable. The arrows always point the way the data is going, which is the one thing about this syntax that was designed to help you.

Two details in that program worth copying. The prompt has a space at the end and no \n, so the cursor waits on the same line, right where the reader is about to type. And the variable is created before it’s read into, because cin needs somewhere to put the answer.

The first surprise

Run that program and type your full name:

What is your name? Ada Lovelace
Hello, Ada!

Half your name vanished. >> reads one word. It stops at the first space and leaves everything after it sitting in the queue, waiting for the next read that never comes.

That’s not a bug, it’s the job description. >> is for reading one item at a time, and it decides where an item ends by looking for whitespace. Handy for numbers. Useless for names.

Reading a whole line

When you want everything up to the end of the line, spaces included, use getline:

std::string name;
std::getline(std::cin, name);
What is your name? Ada Lovelace
Hello, Ada Lovelace!

Notice the different shape. getline isn’t attached to cin with a dot. It’s a standalone function that you hand two things: where to read from, and where to put the result. Chapter 9 explains why some things are written thing.action() and others action(thing); for now, copy the shape.

Numbers work the same way

std::cout << "How old are you? ";
int age = 0;
std::cin >> age;

>> reads the digits, stops at the space or newline after them, and turns them into a number for you. This is exactly where >> is good.

The trap

Now put those two together, in the order you’d naturally write them:

std::cout << "How old are you? ";
int age = 0;
std::cin >> age;

std::cout << "What is your full name? ";
std::string name;
std::getline(std::cin, name);

std::cout << "age=[" << age << "] name=[" << name << "]\n";

Type 36, press return, and the program doesn’t even wait for the name:

How old are you? 36
What is your full name? age=[36] name=[]

The name is empty and you never got to type it.

Here’s why. When you typed 36 and pressed return, two things went into the queue: the characters 36, and the newline from the return key. >> took the digits and stopped. It has no interest in the newline, so it left it there.

Then getline ran. Its job is to read up to the end of a line, and the very first thing it found was the end of a line. So it did exactly what you asked: it read nothing, decided the line was over, and moved on.

Exercise 1 · Cause it, then cure it

Write the age-then-name program without std::cin.ignore() and watch the name come back empty. Then add the ignore() and watch it work.

Doing it in that order matters. When this happens to you in three weeks’ time in a program you care about, you want to recognise the symptom instantly rather than work it out from scratch.

When the reader types nonsense

Programs meet people. People type words into number boxes:

std::cout << "How many? ";
int count = 0;
std::cin >> count;
std::cout << "count=[" << count << "]\n";

std::cout << "Next word? ";
std::string word = "unchanged";
std::cin >> word;
std::cout << "word=[" << word << "]\n";

Type abc and you get this:

How many? abc
count=[0]
Next word? word=[unchanged]

Two things went wrong, and the second is worse. count came back as 0, a perfectly plausible number that you did not get told was made up. And then the next read didn’t happen at all: word still holds what you gave it. cin gave up when it hit letters where digits should be, and it stays given-up for every read that follows.

Check yourself

1. Your program does std::cin >> name; and the reader types "Ada Lovelace". What lands in name?

Not quite. That is what getline would give you. >> stops at the first space.

Yes. Then it stops. "Lovelace" is still in the queue waiting for a read that may never come.

Not quite. Nothing fails. >> reads one word successfully and leaves the rest for later.

2. After std::cin >> age;, a std::getline comes back empty without waiting. Why?

Not quite. Order is not the rule. They mix fine once you deal with what >> leaves behind.

Yes. >> takes the digits and stops. getline then finds an end-of-line immediately and reports an empty line, correctly.

Not quite. Input waits in a queue; nothing is lost by typing speed. What is there is a newline nobody consumed.

3. Someone types "abc" when your program does std::cin >> count; on an int. What happens?

Not quite. It would almost be kinder. C++ carries on quietly instead.

Yes. The plausible-looking zero is the dangerous part, and cin stays in its failed state until something clears it. Chapter 17 covers that.

Not quite. Nothing asks again unless you write the code that asks again. That needs if and a loop.

Project

Mad-libs

Roughly 40 minutes

Ask the reader for a few words without telling them what they’re for, then print the result. The comedy is in the gap between the two.

Collect at least:

  • a name, using getline so two-word names survive
  • a number, using >>
  • a plural noun, using getline, which is the one that will bite you

Then print a short story with all of them in it:

A name: Ada Lovelace
How many? 12
A plural noun: rubber ducks

Ada Lovelace woke up and found 12 rubber ducks in the kitchen.

The point of the project is the ordering problem. You have a >> for the number sitting between two getlines. Write it the obvious way first, run it, and watch the third prompt get skipped. Then put std::cin.ignore() in the right place.

If you write it correctly first time, delete the ignore() and run it anyway. Seeing the failure is worth more than avoiding it.

Stretch: ask for two more words and make the story longer. Notice that every question is the same four lines with a different prompt and a different variable (printing, declaring, reading, and remembering to ignore()). That repetition is the itch chapter 9 scratches.