章节目录

使用istream处理输入

本节阅读量:

iostream库相当复杂——因此我们无法在这些教程中完整介绍它。这里会向您展示最常用的功能。在本节中,我们将研究输入类(istream)的各个方面。


提取运算符

在许多课程中都可以看到,可以使用提取运算符(»)从输入流中读取信息。C++为所有内置数据类型预定义了提取操作,并且之前我们也介绍了如何为自定义类重载提取运算符。

读取字符串时,提取运算符的一个常见问题是如何防止输入溢出缓冲区。以下示例:

1
2
char buf[10]{};
std::cin >> buf;

如果用户输入18个字符会怎么样?缓冲区会溢出,导致不可预期的行为。一般来说,假设用户一定会按预期格式输入字符是有问题的。

处理此问题的一种方法是使用操纵器。操纵器是与提取(>>)或插入(<<)运算符一起使用、用于修改流行为的对象。您已经使用过的一个操纵器是“std::endl”,它既打印换行符,又清空缓冲区。C++提供了一个名为setw(位于iomanip头文件中)的操纵器,可用于限制从流中读入的字符数。要使用setw(),只需提供读取的最大字符数作为参数,并将其插入到输入语句中,如下所示:

1
2
3
#include <iomanip>
char buf[10]{};
std::cin >> std::setw(10) >> buf;

该程序现在只会从流中读取前9个字符(为终止符留出空间)。任何剩余字符都会保留在流中,直到下一次提取。


提取和空白

提醒一下,提取运算符会跳过空白字符(空格、制表符和换行符)。

看看下面的程序:

1
2
3
4
5
6
7
8
int main()
{
    char ch{};
    while (std::cin >> ch)
        std::cout << ch;

    return 0;
}

当用户输入以下内容时:

1
Hello my name is Alex

提取运算符会跳过空格和换行符。因此,输出是:

1
HellomynameisAlex

通常,我们希望获取用户输入时,不应该丢弃空白字符。

为此,istream类提供了许多函数。最有用的一个是get()函数,它可以从输入流中获取原始字符。

下面是与上面相同的程序,使用get():

1
2
3
4
5
6
7
8
int main()
{
    char ch{};
    while (std::cin.get(ch))
        std::cout << ch;

    return 0;
}

现在,当我们输入:

1
Hello my name is Alex

输出是:

1
Hello my name is Alex

还有一个字符串版本,它需要设置要读取的最大字符数量:

1
2
3
4
5
6
7
8
int main()
{
    char strBuf[11]{};
    std::cin.get(strBuf, 11);
    std::cout << strBuf << '\n';

    return 0;
}

如果我们输入:

1
Hello my name is Alex

输出是:

1
Hello my n

注意,我们只读取前10个字符(必须为终止符保留一个字符)。其余字符留在输入流中。

关于get()需要注意的一件重要事情是,它不读取换行符!这可能会导致一些意外的结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int main()
{
    char strBuf[11]{};
    // 最多读取10个字符
    std::cin.get(strBuf, 11);
    std::cout << strBuf << '\n';

    // 再最多读取10个字符
    std::cin.get(strBuf, 11);
    std::cout << strBuf << '\n';
    return 0;
}

如果用户输入:

1
Hello!

程序将打印:

1
Hello!

然后终止!它为什么不再多读取10个字符?答案是第一个get()读取到换行符后停止。第二个get()看到cin流中仍然有输入,并试图读取它。但第一个字符就是换行符,因此它立即停止。

因此,还有一个名为getline()的函数,它的工作方式类似于get(),但会提取(并丢弃)分隔符。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int main()
{
    char strBuf[11]{};
    // 最多读取10个字符
    std::cin.getline(strBuf, 11);
    std::cout << strBuf << '\n';

    // 再最多读取10个字符
    std::cin.getline(strBuf, 11);
    std::cout << strBuf << '\n';
    return 0;
}

此代码将按预期执行,即使用户输入的字符串中包含换行符。

如果需要知道上次调用getline()提取了多少字符,请使用gcount():

1
2
3
4
5
6
7
8
9
int main()
{
    char strBuf[100]{};
    std::cin.getline(strBuf, 100);
    std::cout << strBuf << '\n';
    std::cout << std::cin.gcount() << " characters were read" << '\n';

    return 0;
}

gcount()包括任何提取和丢弃的分隔符。


用于std::string的getline()的特殊版本

getline()有一个特殊版本,位于istream类之外,用于将数据读取到std::string。这个特殊版本不是ostream或istream的成员,定义在string头文件中。下面是它的用法示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <string>
#include <iostream>

int main()
{
    std::string strBuf{};
    std::getline(std::cin, strBuf);
    std::cout << strBuf << '\n';

    return 0;
}

几个更有用的istream函数

还有几个您可能会用到的输入函数:

  1. ignore() 丢弃流中的第一个字符。
  2. ignore(int nCount) 丢弃前nCount字符。
  3. peek() 允许您从流中读取字符,而不将其从流中删除。
  4. unget() 将最后从流中读取的一个字符放回去,以便下次调用可以再次读取它。
  5. putback(char ch) 允许您将所选的字符放回流中,以便下一次调用读取。

istream包含许多其他函数,以及上述函数的变体。它们是否有用,取决于您需要执行的操作。您可以在这里找到对应文档。


28.0 输入和输出(I/O)流

上一节

28.2 使用ostream和ios输出

下一节