C++ 中cin的输入原理及字符串(string、char[])输入问题

365网站手机打不开 admin 2025-07-29 17:10:10 阅读 9052

目录

一、cin 是 C++ 封装的一个类对象

二、对字符数组的输入方法(char str[N]):

1、cin 使用空白字符作为一次输入的结尾,并忽略该空字符。

2、使用 getline() / get() 函数完成面向行的输入

3、数字与字符串的混合输入问题

三、对 string 对象的输入方法

C++中常用的输入方式为 cin 。通过 cin 来获取字符串是常用的操作,但是字符串中往往可能会包含空格等空白字符,而在一般的输入场景中,由于不能通过键盘输入空字符 '\0'(在存储中,字符串都以空字符 '\0' 结尾),因此 cin 使用空白字符(空格、制表符和换行符)来确定字符串的结束位置,因此想要输入空格必须得修改输入的方式。

一、cin 是 C++ 封装的一个类对象

C或C++并没有将输入输出建立在语言中,也就是说像 int、if、for ...等等这些都是C++的关键字,但其中的I/O语句 cin / cout 并不在C++的关键字列表中,也就不是 C++ 的关键字了。

实际上cin / cout 是C++在头文件中封装的类对象,cin 是 istream类对象,cout是ostream类对象,其输入输出操作符 cin>> 和 cout<< 也是通过重载机制赋予的输入输出功能。

使用 cin 执行输入操作是有一个输入缓冲区的,如果是从键盘获取的输入数据,则在按下 Enter 键(即回车键)之后输入的数据(包括回车键)才会被送到输入缓冲区中执行实际的 I/O 操作。

如:我们使用 cin 输入 1[空格]2[空格]3[空格]4[空格]5[回车]。则实际上送入缓冲区的是 1 2 3 4 5'\n'。注意只有最后一个回车按下去后输入数据才会被送到输入缓冲区。

输入:1[空格]2[空格]3[空格]4[空格]5[回车]

则实际上送入缓冲区的是: 1 2 3 4 5'\n' ,共10个字符。

为什么会设置输入/输出缓冲区?我从《C++ Primer Plus 第六版》的有关介绍中总结了两点原因:

1、如果每次读入一个字符都直接处理,由于从磁盘文件中读取一个字符需要大量硬件活动,在读取量较大时,这是很耗时间的(硬盘的读写速度远小于内存和CPU)。缓冲的方式是每次从硬盘中读取大量信息放在内存中,然后每次从缓冲区读取字节仅从操作,由于内存的读写速度远高于硬盘,因此这样可以大大提升读写效率。

2、由于C++程序通常在用户键入回车后刷新缓冲区,因此对键盘输入进行缓冲可以再将输入传给程序之前返回并更正。也就是说在你按下回车键前,你输入的数据都是可以删除并重新输入(修改)的。

这里只介绍了点 cin 的基础原理,用于理解后面的内容,详细内容就不展开细说了。

二、对字符数组的输入方法(char str[N]):

1、cin 使用空白字符作为一次输入的结尾,并忽略该空字符。

int main(void)

{

char line[10];

cout << "input what you want:";

cin >> line; // 可以尝试输入 "123 456"

cout << "what you input is:" << line << endl; // 只能输出 "123"

return 0;

}

/* 测试用例

input what you want:123 456

what you input is:123

*/

也就是说,这种情况下一次只能完成一个单词的输入,其后的单词都会被忽略掉。

2、使用 getline() / get() 函数完成面向行的输入

getline() / get() 函数都可以接收一行字符串输入,并以回车作为输入的结尾。区别就是 getline 会在输入结束后丢弃最后的换行符,而 get 则会在输入队列中保留最后的换行符。

1)cin.getline()

可以通过 cin.getline(line, nums, (optional)delim) 实现调用:从输入队列中获取 nums-1 个字符到 line 中,因为字符串的最后一个字符是 '\0'。如果输入的长度超出 nums-1 个,则 getline 和 get 将把余下的字符留在输入队列中,另外 getline 还会设置失效位,并关闭后面的输入(即后面的所有输入将不可用),此时可以用 cin.clear() 清除标准位就可以恢复后面的输入。

这种句点表示法表明,函数 getline() 是 istream 类的一个方法。另外需要注意的是 cin 是一个 istream 对象。cin.getline() 的第一个参数是目标数组,第二个参数是数组长度,这样来避免超越数组的边界。后面介绍通过输入对 string 赋值的时候调用方式会有区别。

cin.getline(line, nums)

int main(void)

{

char line1[10];

char line2[10];

char line3[10];

cout << "input what you want:" << endl;

cin.getline(line1, 10); // 可以尝试输入 "123 456 789" ('\n') 含有11个字符

// cin.clear();

cin.getline(line2, 10); // 不可用,直接退出

cin.getline(line3, 10); // 不可用,直接退出

cout << "what you input is:" << endl;

cout << "line1:" << line1 << endl; // 输出 "123 456 7"

cout << "line2:" << line2 << endl; // 输出 空

cout << "line3:" << line3 << endl; // 输出 空

return 0;

}

/*

input what you want:

123 456 789

what you input is:

line1:123 456 7

line2:

line3:

*/

// 使用 clear

int main(void)

{

char line1[10];

char line2[10];

char line3[10];

cout << "input what you want:" << endl;

cin.getline(line1, 10); // 可以尝试输入 "123 456 789" ('\n') 含有11个字符

cin.clear(); // 清除标志位

cin.getline(line2, 10); // 可用,输出 "89",这是上一步输入队列中的字符

cin.getline(line3, 10); // 可用,你输入啥输出啥

cout << "what you input is:" << endl;

cout << "line1:" << line1 << endl; // 输出 "123 456 7"

cout << "line2:" << line2 << endl; // 输出 空

cout << "line3:" << line3 << endl; // 输出 空

return 0;

}

/*

input what you want:

123 456 789

123

what you input is:

line1:123 456 7

line2:89

line3:123

*/

cin.getline 还有第三个参数 delim(全称应该是 delimiter,中文意思:定界符/分隔符),作用是指定用作分界符的字符(没有这个参数的版本将以换行符为分界符)。

遇到分界符后,当前字符串的输入将停止,即使还未读取最大数目的字符。因此在默认情况下,这种分界符的方式或者到达输入的指定数目都将停止读取输入。和默认情况一样 get() 将分界符留在输入队列中,而 getline() 不保留。

cin.getline(line, nums, delim),也就是以 delim 指定的字符作为一次字符串输入的结束,其后的在 nums-1 范围内的输入会保留在输入队列中作为下一个字符串的开始。

int main(void)

{

char line1[10];

char line2[10];

cout << "input what you want:" << endl;

cin.getline(line1, 10, 's'); // 可以尝试输入 "123s456" ('\n')

cin.getline(line2, 10); //

cout << "what you input is:" << endl;

cout << "line1:" << line1 << endl; // 输出 "123"

cout << "line2:" << line2 << endl; // 输出 "456"

return 0;

}

/* 测试用例

input what you want:

123s456

what you input is:

line1:123

line2:456

*/

getline 函数每次读取一行,它通过换行符来确定行尾,但不保存换行符。相反,在存储字符串时他用空字符 '\0' 来替换换行符。

2)cin.get()

调用方式类似 getline,即cin.get(line, nums, (optional)delim)

前面说过,get 函数会在输入队列中保留最后的换行符。注意是输入队列,也就是在一个 get()

函数完成输入后,会在输入缓存中保留组后的换行符,如果不做处理在下次运行 get() 时会出问题。

int main(void)

{

char line1[10];

char line2[10];

cout << "input what you want:" << endl;

cin.get(line1, 10); // 可以尝试输入 "123" ('\n'), 此时回车符将会留在输入队列中

cin.get(line2, 10); // 可以尝试输入 "456",可以发现 line1 输入完后,这步直接被“跳过了”

cout << "line1:" << line1 << endl; // 输出 "123"

cout << "line2:" << line2 << endl; // 输出空字符,由于 cin.get(line2, 10); 读入的是上一个字符输入结束时留输入队列中的回车符

return 0;

}

/* 测试用例

input what you want:

123

what you input is:

line1:123

line2:

*/

由于第一次调用后换行符留在了输入队列中,因此第二次看到第一个字符便是换行符,因此就直接结束了。如果不借助与任何帮助,get() 将不能跨过该换行符(在多个get的情况下)。可以使用下面这种方式避免这种情况:

cin.get(line2, 10).get();

\\ 或者

cin.get(line2, 10);

get();

3、数字与字符串的混合输入问题

使用 cin 输入一个整数后,最后的回车符会留在输入队列中,类似于get,这会对后续的 getline 和 get 输入产生影响,因为 getline / get 看到换行符后会认为是一个空行,然后会把空字符赋给后面的字符串,如下:

int main(void)

{

int a, b; // 输入 123 ('\n') 456 ('\n')

char s[5];

cin >> a;

cin >> b;

cin.getline(s, 5); // 得到换行符,直接赋空值

cout << a << " " << b << " " << s << endl;

}

/* 测试用例

123

456

123 456

*/

此时可以使用在后面添加 cin.get() 或者 (cin >> b).get() 的方式来解决这个问题:

int main(void)

{

int a, b; // 输入 123 ('\n') 456 ('\n')

char s[7];

cin >> a;

// cin >> b;

// cin.get();

(cin >> b).get();

cin.getline(s, 7); // 输入 "string" ('\n')

cout << a << " " << b << " " << s << endl;

}

/* 测试用例

123

456

string

123 456 string

*/

三、对 string 对象的输入方法

使用 cin<

int main(void)

{

string str;

cout << "input what you want:" << endl;

getline(cin, str);

cout << "what you input is:" << endl;

cout << str << endl;

}

/* 测试用例

input what you want:

123 456 789

what you input is:

123 456 789

*/

注意这里的面向行的输入是 getline(cin, str)

这里没有使用句点表示法,这表明这个 getline 不是类方法。他将 cin 作为参数,指出到哪里去查找输入。另外也没有指出字符串长度的参数,因为 string 对象将根据字符串的长度自动调整自己的大小。

至于为什么字符数组和string对象的 getline 方法不一样呢,其实这是个历史问题。早期的 C++ 还没引入 string 类的时候就有 istream 类了,因此当时也就没有针对 string 对象的输入方式了,所以最后才会演变出这两种方式。

相关文章