Bootstrap

大坑:c++如何输入带空格的字符串?

在读这篇文章之前,先来记住两点:

  • 空字符’\0’,它在内存中占一个字符,但不会影响字符串的长度,就是说我们用strlen()和size()函数返回字符串或字符数组长度的时候是不算空字符的。
  • 输入回车是所有的输入函数的输入结束标志,它不等于空字符,也不等于’\n’字符。'\n’字符在字符串中是占位的。

1. 引言

输入输出是我们接触最多,但也最容易忽略的知识点。相信很多人都有过在输入输出上总是不 ac 的经历,因为输出取决于输入对不对,下面我们就来简单概括一下输入的精髓。

我们就从一道题说起吧:

  • 第一步,输入一个整数 m,并输出
  • 第二步,输入一行带空格的字符串,并输出
  • 第三步,输入多行带空格的字符串,并输出
  • 第四步,输入一个字符 c,并输出

感兴趣的可以自己试一下,如果对输入不熟悉的话,一定会像笔者开始那样懵逼。

下面我们就来给出答案:

#include <bits/stdc++.h>
using namespace std;

int main()
{
	int m;
	cin >> m; //遇到空白(包括回车、空格等)结束输入,并且会把非int字符留在缓存区(包括回车,空格等空白字符)
	cout << m << endl;
	//回收缓存区的第一个字符(任意字符),如果不回收,那么下面的getline()接收到字符串的就是这个换行符
	cin.get(); 
	//输入一行带空白字符的字符串,遇到回车结束输入
	string str;
	getline(cin, str);
	cout << str << endl;
	//输入多行带空白字符的字符串,如果当前行只输入回车结束输入
	string str1;
	vector<string> vec;
	while (getline(cin, str1)) {
		if (str1.size() != 0) { //当前行只输入回车
			vec.push_back(str1);
		}
		else {
			break;
		}
	}
	for (int i = 0; i < vec.size(); i++) {
		cout << vec[i] << endl;
	}
	//输入一个字符,能够正常输出,可以看到getline()之后缓存区没有任何字符了,这也印证了getline()不会把回车留在缓冲区
	char c;
	cin >> c;
	cout << c << endl;
	return 0;
}

在这里插入图片描述

2. 输入常用 API

2.1 cin

cin是 c++ 中最常用的输入语句,当遇到空白字符(空格或者回车键)即停止,并且会把空白字符留在缓冲区,容易造成数据的输入错误。

2.2 cin.get()

接收缓冲区的第一个字符,通常用来接收由cin产生的空白字符

2.3 cin.get(char *str, int maxnum)

参数说明:

  • str:接收输入的字符串
  • maxnum:最大可接受的字符数量,它必须小于等于str.size(),否则报错

从 2.2 就能看出来cin.get可以接收空格。

但这个函数很危险,因为它在缓冲区中创建了str.size()个字节的空间,如果我们输入的字符数量不够str.size(),那剩下的那段空间依然不会释放,如果我们后面还有输入字符或者字符串,就会优先接收缓冲区中的那段剩余字符串,也就是空白字符串"\0\0\0…"。

所以,这个函数不推荐使用,尤其是需要连续输入多行带空格的字符串的时候。

给两个例子对比就很清晰了。

int main()
{
	//输入两行带空格的字符串
	char str1[1024];
	cin.get(str1, 1024);
	cout << str1 << endl;
	char str2[1024];
	cin.get(str2,1024);
	cout << str2 << endl;
	return 0;
}

在这里插入图片描述

说明:上述例子中,cin.get(str1, 1024)在缓冲区开辟了1024个字节的空间,str1只接受了输入那一行的字符串那么多个字符。剩下的1000多个空白字符就都给了str2了,所以str2相当于直接遇到回车结束输入了,接收不到任何字符(字符串的长度为零)。

int main()
{
	//输入两行带空格的字符串
	char str1[5];
	cin.get(str1, 5);
	cout << str1 << endl;
	char str2[1024];
	cin.get(str2,1024);
	cout << str2 << endl;
	return 0;
}

在这里插入图片描述

说明:上述例子中,str1接收了"abcd"四个字符和一个’\0’,然后剩下的一个e被str2接收了。

2.4 getline(cin,str)

getline()可以输入一行,能接收空格,头文件是<string>,所以用string来接收。遇到回车结束输入。这个函数适合用来输入带空格字符串,尤其是对于要连续输入多行带空格的字符串的时候,这个函数是最最最最合适的。

int main()
{
    string str;
    getline(cin,str); //getline需包含<string>
    cout << str << endl;
    return 0; 
}   

输入连续多行字符串请看最开始那个例子就行。

2.5 cin.getline(char *str, int maxnum)

这个函数的功能其实跟getline()完全一样,区别就是getline()是string流,而cin.getline(char *str, int maxnum)是istream流。所以在使用的时候是可以替换getline()的。

它不像cin.get(char *str, int maxnum)那样在缓冲区开辟str.size()的数据,而是输入多少就开辟多少,不会对后面的字符串输入产生任何影响。

int main()
{
	//输入两行带空格的字符串
	char str1[1024];
	cin.getline(str1, 1024);
	cout << str1 << endl;
	char str2[1024];
	cin.get(str2,1024);
	cout << strlen(str2) << endl;
	return 0;
}

在这里插入图片描述

int main()
{
	//输入多行带空白字符的字符串,如果当前行只输入回车结束输入
	char str1[1024];
	vector<string> vec;
	while (cin.getline(str1, 1024)) {
		if (strlen(str1) != 0) { //当前行只输入回车
			vec.push_back(string(str1));
		}
		else {
			break;
		}
	}
	for (int i = 0; i < vec.size(); i++) {
		cout << vec[i] << endl;
	}
	return 0;
}

在这里插入图片描述

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;