18.2 使用 istream 输入¶
By Alex on March 4th, 2008 | last modified by nascardriver on August 3rd, 2020
翻译by dashjay 2020-08-10
iostream
库是相当复杂的 —— 因此在这些教程中我们将不会有能力去覆盖全面。然而我们将会展示给你最常用的函数。在这次课中,我们将会学习输入这块内容(istrea)。
提取操作符¶
正如现在很多课程中所看到的那样,我们可以使用提取操作符 (>>) 来从输入流读取信息。C++ 对所有内建的数据类型都有预定义的提取操作符,并且你早已知道如何为自己的类重载提取运算符来直接输出。
当读取字符串时,一个常见是如何避免输入越界。这有一个例子:
char buf[10];
std::cin >> buf;
如果用户输入 18 个字符串会发生什么?缓冲区溢出,糟糕的东西会发生。大体上讲,假设用户会输入多少字符不是一个好办法。
有一个通过操纵器(manipulators)解决此问题的方法。一个 manipulators 是一个对象用来修改一个流,当使用 提取(>>) 或插入(<<)操作符时。一个你早就使用过的 manipulators 就是 std::endl
,它可以打印一个新行,并且刷新任何已经缓存的输出。C++ 提供了一个 manipulators 叫做 setw
(在 iomanip.h 头中)可以被用来限制读取的字符数量。要使用 setw()
,简单的提供一个最大的要读取字符数量作为参数,并且插入到你的输入语句中,像这样:
#include <iomanip.h>
char buf[10];
std::cin >> std::setw(10) >> buf;
This program will now only read the first 9 characters out of the stream (leaving room for a terminator). Any remaining characters will be left in the stream until the next extraction. 这个程序现在将会读取从流中读取前9个字符(留一个空间给终止符)。任何其他的字符都会被留在流中,指导下次提取。
提取和空格¶
一件到目前为止我们都忘了提起的事情,就是提取操作符和 ”格式化“ 后的数据工作 ———— 是的,它会跳过空白(空白,制表符,新行)。
看一眼下列程序:
int main()
{
char ch;
while (std::cin >> ch)
std::cout << ch;
return 0;
}
当用户输入下列字符时:
Hello my name is Alex
提取操作符跳过了空格和新行,因此输出是:
HellomynameisAlex
通常,你将会想获取用户输入但是一起空格。为了这么做,istream
类提供了许多函数可以函数。
最有用的就是 get()
函数,可以简单的从输入流中获取一个字符,下面是一个和上方具有用样功能的简单例子,但是它使用 get()
int main()
{
char ch;
while (std::cin.get(ch))
std::cout << ch;
return 0;
}
现在我们使用下列输入:
Hello my name is Alex
输出则是:
Hello my name is Alex
std::get()
也有一个 string
版本,可以传入一个最大字符数来读取:
int main()
{
char strBuf[11];
std::cin.get(strBuf, 11);
std::cout << strBuf << '\n';
return 0;
}
如果你输入:
Hello my name is Alex
输出则是:
Hello my n
注意到我们仅仅读取了前 10 个字符(我们不得不为终止符留一个空)。剩下的字符会留在输入流中。
一件有关 get()
的重要的事是他不能读取一个新行字符!这可能会得到一些意料之外的结果
int main()
{
char strBuf[11];
// Read up to 10 characters
std::cin.get(strBuf, 11);
std::cout << strBuf << '\n';
// Read up to 10 more characters
std::cin.get(strBuf, 11);
std::cout << strBuf << '\n';
return 0;
}
如果用户输入:
Hello!
程序将会输出
Hello!
然后就突然中止了!为什么它不询问另外10个字符呢?答案就是因为第一次 get()
读取到了新行,然后停止了。第二个 get()
看到这依然想要读取。但是第一个字符是一个新行。因此它立刻中止。
因此,这有另一个函数叫做 getline()
能够想 get()
那样工作,但是也能读取一个新行。
int main()
{
char strBuf[11];
// Read up to 10 characters
std::cin.getline(strBuf, 11);
std::cout << strBuf << '\n';
// Read up to 10 more characters
std::cin.getline(strBuf, 11);
std::cout << strBuf << '\n';
return 0;
}
这端代码将会像你所期待的那样工作,即使用户输入一个带有新行的字符串。
如果你需要知道上次 getline()
提取了多少字符,用 gcount()
:
int main()
{
char strBuf[100];
std::cin.getline(strBuf, 100);
std::cout << strBuf << '\n';
std::cout << std::cin.gcount() << " characters were read" << std::endl;
return 0;
}
一个为 std::string
提供的 getline()
有一个特殊版本的 getline()
在 istream 类的外面,被用来读取到 std::stirng
中。这个特殊版本的函数不是 ostream
也不是 istream
的成员。并且它被包含在 string
头部中。这是一个使用它的例子:
# include <string>
# include <iostream>
int main()
{
std::string strBuf;
std::getline(std::cin, strBuf);
std::cout << strBuf << '\n';
return 0;
}
一些有用的输入函数¶
这里有一些有用的输入函数你可能会使用:
ignore()
遗弃流中的第一个字符。
ignore(int nCount)
遗弃前 nCount 个字符。
peek()
允许从流中读取一个字符,并且不将他从流中移除
unget()
归还最后一个字符,使得它可以可以下次再被读取。
pushback(char ch)
允许你放置一个你选择的字符进入流中,以便下次调用时读取。
根据你的工作,istream 包含了许多其他的函数和变量可能非常有用。然而那些话题更加适合出现在一个教程或者专注于标准库的书中(例如 “The C++ Standard Library” by Nicolai M. Josuttis)