从键盘读取文本时,如果输入的内容包含空格,比如英文姓名 "Jack Smith" 或短句 "Hello lvyenet",直接使用 std::cin >> 只能读到第一个空格前面的单词。
如果想要把包含空格的一整行内容完整读取出来,在 C++ 中可以使用 <string> 头文件为 std::string 提供的 std::getline() 函数。
cin >> 读取字符串的局限
在学习 std::getline() 之前,我们先来看看使用 std::cin 读取字符串时的具体表现。
对于字符串来说,>> 运算符是按 “空白字符” 来切分输入内容的,比如普通空格,以及换行符 \n、制表符 \t 等转义字符。一旦在读取过程中遇到空白字符,std::cin >> 就会认为当前这个字符串已经读完了,并立即停止读取。
示例 1:cin >> 遇到空格停止
#include <iostream>
#include <string>
int main() {
std::string text;
std::cout << "请输入一句话:";
std::cin >> text;
std::cout << text << '\n';
return 0;
}运行之后,当我们输入 “Hello lvyenet”,其输出结果如下。
请输入一句话:Hello lvyenet
Hello分析:
在这个例子中,虽然我们在键盘上输入的是 "Hello lvyenet",但变量 text 最终只拿到了 "Hello"。
这是因为 std::cin >> 读到第一个空格时就停止读取了,空格后面的 "lvyenet" 则被留在了输入缓冲区里。因此,std::cin >> 只适合读取不带空格的单个词,无法读取包含空格的整行句子。
C++ getline() 读取整行
为了解决遇到空格就被截断的问题,<string> 头文件提供了 std::getline() 函数。它会从输入流中连续读取所有字符,包括句子中间的空格,一直读到按下 Enter 键产生的换行符 \n 为止。
语法:
std::getline(std::cin, 字符串变量);说明:
其中,getline 是 “get line(获取一行)” 的意思,调用时需要传入以下 2 个参数:
- std::cin(必选):表示从键盘标准输入读取数据,需要引入
<iostream>头文件。 - 字符串变量(必选):用来保存读取结果的
std::string变量,需要引入<string>头文件。
需要注意的是,std::getline() 读到行尾时,会自动把末尾的换行符 \n 从输入流中取走并丢弃,不会把换行符存进字符串变量里。
示例 2:读取带空格的整行文本
#include <iostream>
#include <string>
int main() {
std::string text;
std::cout << "请输入一句话:";
std::getline(std::cin, text);
std::cout << text << '\n';
return 0;
}运行之后,当我们输入 “Hello lvyenet”,其输出结果如下。
请输入一句话:Hello lvyenet
Hello lvyenet分析:
std::getline(std::cin, text); 表示把整行输入全部读入 text 中,两个单词中间的空格也完整地保留了下来。
除了能读取空格之外,std::getline() 还支持直接读取空行。如果我们在输入时什么文字都不写,直接按下 Enter 键,它会立即结束读取并得到一个空字符串 "",而不会像 std::cin >> 那样一直停在终端等待输入。下面再来看一个例子。
示例 3:getline() 读取空行
#include <iostream>
#include <string>
int main() {
std::string remark;
std::cout << "请输入备注:";
std::getline(std::cin, remark);
std::cout << "[" << remark << "]" << '\n';
return 0;
}运行之后,当我们直接按下 Enter 键,其输出结果如下。
请输入备注:
[]分析:
直接按下 Enter 键时,输入流里只有一个换行符 \n。std::getline() 遇到这个换行符后,认为当前这一行已经读完了,于是把空字符串 "" 存入 remark 中,两侧加上中括号后的输出结果为 []。
cin >> 与 getline() 混用问题
单独使用 std::getline() 时非常简单。初学的小伙伴最容易踩坑的地方,是前面先使用了 std::cin >> 读取数字或单词,紧接着又在后面调用 std::getline()。
1. 为什么 getline() 会被直接跳过?
当我们把 std::cin >> 和 std::getline() 直接连在一起写时,运行程序往往会出现一个奇怪的现象:输入完第一个数据按下回车后,程序根本不等我们输入后面的字符串,就直接运行结束了。请看下面例子。
示例 4:混用 cin >> 与 getline()
#include <iostream>
#include <string>
int main() {
int age = 0;
std::string name;
std::cout << "请输入年龄:";
std::cin >> age;
std::cout << "请输入姓名:";
std::getline(std::cin, name);
std::cout << "年龄:" << age << '\n';
std::cout << "姓名:[" << name << "]" << '\n';
return 0;
}运行之后,当我们输入 “20” 并按下 Enter 键,其输出结果如下。
请输入年龄:20
请输入姓名:年龄:20
姓名:[]分析:
咦?为什么还没来得及输入姓名,name 就直接变成空字符串了,而且输出的 “年龄:20” 还跟 “请输入姓名:” 挤在了同一行呢?
这其实跟临时存放键盘输入内容的 “输入缓冲区” 有关。我们按执行顺序来看看缓冲区里发生了什么:
- 输入数据:我们在键盘上输入
20并按下 Enter 键,此时输入缓冲区里实际存入的是"20\n"。 - 执行 cin >>:
std::cin >> age只负责把前面的整数20读走,而按下 Enter 键产生的换行符'\n'依然残留在输入缓冲区里。 - 执行 getline():紧接着调用
std::getline(std::cin, name)时,它直接遇到了上一行残留的'\n',误以为我们已经按下了回车。于是它读出空字符串""并立即结束,没有停下来等待输入。
正因如此,终端没有停顿换行,后面的 “年龄:20” 直接紧贴着 “请输入姓名:” 打印在了同一行。整个读取与换行符残留的过程如下图所示。

2. 使用 cin.ignore() 清除残留换行符
弄清楚原因之后,解决办法就很简单了:在 std::cin >> 和 std::getline() 之间,调用 std::cin.ignore() 把输入缓冲区里残留的换行符清理掉即可。
语法:
std::cin.ignore(最大字符数, 停止字符);说明:
其中,ignore 在英文中是 “忽略、丢弃” 的意思。std::cin.ignore() 会从输入缓冲区中依次丢弃多余的字符:只要丢弃的数量达到设定的上限,或者中途遇到并丢弃了指定的停止字符,就会立即停止清理。
平时清理当前行残留的换行符时,我们一般写成 std::cin.ignore(1000, '\n')。第 1 个参数只是设定一个足够大的清理上限,第 2 个参数 '\n' 才是真正的停止标志,所以平时写 100 或 1000 效果都是一样的。
示例 5:清除残留的换行符
#include <iostream>
#include <string>
int main() {
int age = 0;
std::string name;
std::cout << "请输入年龄:";
std::cin >> age;
// 清除当前行残留的换行符
std::cin.ignore(1000, '\n');
std::cout << "请输入姓名:";
std::getline(std::cin, name);
std::cout << "年龄:" << age << '\n';
std::cout << "姓名:[" << name << "]" << '\n';
return 0;
}运行之后,当我们依次输入 “20” 和 “Jack Smith”,其输出结果如下。
请输入年龄:20
请输入姓名:Jack Smith
年龄:20
姓名:[Jack Smith]分析:
在这个例子中,std::cin.ignore(1000, '\n'); 会把输入缓冲区里上一行残留的换行符 '\n' 丢弃掉。缓冲区清理干净后,后面的 std::getline() 就能正常等待我们输入 "Jack Smith" 了。
在清理残留换行符时,有时大家也会看到不传参数的写法:
// 写法 1
std::cin.ignore();
// 写法 2(推荐)
std::cin.ignore(1000, '\n');其中,写法 1 默认只丢弃一个字符,而写法 2 会一直清理到当前行的换行符为止。如果用户在输入数字 20 后直接按了回车,两种写法的效果是一样的。
但如果用户在输入 20 后不小心多敲了几个空格才按回车,写法 1 只会丢掉第一个空格,而写法 2 能把多余的空格连同换行符一起清理干净。因此在实际开发中,我们更推荐使用写法 2。
注意
混用 cin >> 与 getline() 时:只有前面刚使用过 std::cin >>,紧接着又要调用 std::getline() 时,中间才需要加上 std::cin.ignore(1000, '\n') 清理残留的换行符。
连续调用多次 getline() 时:由于每次 std::getline() 都会自动把行尾的换行符取走丢弃,两次调用之间千万不要加 ignore(),否则反而会把下一行输入的开头字符吞掉。
