28-2 使用 istream 输入

iostream 库相当复杂,因此我们无法在本教程中全面讲解。不过,我们会向您展示最常用的功能。在本节中,我们将探讨输入类 (istream) 的各个方面。

提取运算符

正如我们在许多课程中看到的,我们可以使用提取运算符 (>>) 从输入流中读取信息。C++ 为所有内置数据类型预定义了提取操作,而且您已经了解了如何为自己的类重载提取运算符。

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

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

如果用户输入 18 个字符会发生什么?缓冲区溢出,后果不堪设想。一般来说,对用户会输入多少个字符做任何假设都是不明智的。

解决这个问题的一种方法是使用操纵符。操纵符是一个对象,当与提取运算符 (>>) 或插入运算符 (<<) 一起使用时,可以用来修改流。你已经使用过的一个操纵符是 std::endl,它既可以打印换行符,也可以刷新所有缓冲的输出。C++ 提供了一个名为setw 的操纵符(位于 iomanip 头文件中),可以用来限制从流中读取的字符数。要使用 setw(),只需将要读取的最大字符数作为参数提供,并将其插入到你的输入语句中,如下所示:

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

该程序现在只会读取数据流中的前 9 个字符(留出终止符的空间)。剩余的字符将保留在数据流中,直到下次提取。

提取和空白

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

请查看以下程序:

int main()
{
    char ch{};
    while (std::cin >> ch)
        std::cout << ch;

    return 0;
}

当用户输入以下内容时:

Hello, my name is Rock!;

提取运算符会忽略空格和换行符。因此,输出结果为:

img

很多时候,你需要获取用户输入,但又不想丢弃空格。为此,istream 类提供了许多可用于此目的的函数。

其中最有用的函数之一是get()函数,它可以从输入流中获取一个字符。以下是使用 get() 函数实现的与上面相同的程序:

int main()
{
    char ch{};
    while (std::cin.get(ch))
        std::cout << ch;

    return 0;
}

现在当我们使用输入时:

Hello, my name is Rock!

输出结果为:

img

get() 函数还有一个字符串版本,它接受的最大字符数为:

int main()
{
    char strBuf[11]{};
    std::cin.get(strBuf, 11);
    std::cout << strBuf << '\n';

    return 0;
}

如果我们输入:

Hello, my name is Rock!

输出结果为:

img

请注意,我们只读取了前 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!

程序将输出:

img

然后就终止了!为什么它没有请求再读取 10 个字符呢?答案是,第一个 get() 函数读取到换行符(newline)就停止了。第二个 get() 函数发现 cin 流中还有输入,于是尝试读取。但是第一个字符是换行符,所以它立即停止了。

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

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;
}

img

即使用户输入的字符串中包含换行符,这段代码也能按预期运行。
如果您需要知道上次调用 getline() 提取了多少个字符,请使用gcount()

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;
}

img

gcount() 包括所有已提取和已丢弃的分隔符。

针对 std::string 的特殊版本的 getline() 函数

有一个特殊的 getline() 函数版本存在于 istream 类之外,用于读取 std::string 类型的变量。这个特殊版本既不是 ostream 类的成员,也不是 istream 类的成员,而是包含在字符串头文件中。以下是一个使用示例:

#include <string>
#include <iostream>

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

    return 0;
}

img

还有一些其他有用的 istream 函数

还有一些其他有用的输入函数,您可能也想用到:

ignore()会丢弃流中的第一个字符。
ignore(int nCount)会丢弃前 nCount 个字符。
peek ()允许你从流中读取一个字符,但不会将其从流中移除。
unget() 会将最后读取的字符放回流中,以便下次调用时可以再次读取。putback(char ch) 允许你将选择的字符放回流中,以便下次调用时可以再次读取。

istream 包含许多其他函数以及上述函数的变体,根据您的需要,这些函数和变体可能很有用。您可以在参考网站(例如https://en.cppreference.com/w/cpp/io/basic_istream )上找到这些函数和变体。

posted @ 2025-12-06 22:17  游翔  阅读(6)  评论(0)    收藏  举报