C++使用 istream 进行输入

<iostream> 库功能庞杂,本节仅介绍最常用的部分。下面围绕输入类 istream 展开说明。

提取运算符(>>

如前面课程所示,可使用提取运算符(>>)从输入流读取数据。C++ 为所有内置类型预定义了提取操作,也可为自己的类重载该运算符。

用提取运算符读入字符串时,常遇到缓冲区溢出问题:

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

若用户输入 18 个字符,缓冲区将溢出,导致未定义行为。一般而言,绝不要假设用户会输入多少字符。

一种解决方法是使用操控符(manipulator)。操控符是可与 >><< 联用以修改流行为的对象。你已使用过 std::endl,它既输出换行又刷新缓冲区。C++ 提供 setw(位于 <iomanip>)可限制读取字符数:

#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 Alex

输出:

HellomynameisAlex

若想保留空白符,可使用 istream 提供的成员函数,其中最常用的是 get()

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

输入同上,输出:

Hello my name is Alex

get() 还有字符串版本,可指定最大读取字符数:

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]{};
    std::cin.get(strBuf, 11); // 读取至多 10 字符
    std::cout << strBuf << '\n';
    std::cin.get(strBuf, 11); // 再读取至多 10 字符
    std::cout << strBuf << '\n';
    return 0;
}

输入:

Hello!

输出:

Hello!

随后程序结束。原因是第一次 get() 读到换行即停止,第二次 get() 看到换行也立即返回,未再读取。

因此,可使用 getline(),它与 get() 类似,但会读取并丢弃分隔符(换行):

int main()
{
    char strBuf[11]{};
    std::cin.getline(strBuf, 11);
    std::cout << strBuf << '\n';
    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" << '\n';
    return 0;
}

gcount() 包含被提取且丢弃的分隔符。

std::string 专属 getline()

头文件 <string> 提供了非成员 getline(),用于读取 std::string

#include <string>
#include <iostream>

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

更多有用的 istream 函数

  • ignore():丢弃流中第一个字符。
  • ignore(int nCount):丢弃前 nCount 个字符。
  • peek():预览下一个字符但不提取。
  • unget():将最后读取的字符放回流中,供下次读取。
  • putback(char ch):将指定字符放回流中,供下次读取。

istream 还包含许多其他函数及其变体,具体需求可查阅参考网站,如 cppreference.com

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"