一、概述
输入和输出功能并未定义为核心 C++ 语言的一部分,而是通过 C++ 标准库提供(因此位于 std 命名空间中)。
包含iostream 库头文件并使用 cin 和 cout 对象来执行简单的 I/O。
二、iostream 库
关于这种层次结构,您可能会注意到的第一件事是它使用多重继承(我们告诉您尽可能避免这种情况)。 但是,为了避免任何典型的多重继承问题,iostream 库已经过精心设计和广泛测试,因此您可以自由使用它而不必担心。
1、Streams
C++ 中的 I/O 是通过流实现的。 抽象地说,流只是可以按顺序访问的字节序列。 随着时间的推移,流可能会产生或消耗可能无限量的数据。
通常我们处理两种不同类型的流。 输入流用于保存来自数据生产者的输入,例如键盘、文件或网络。 例如,用户可以在程序当前不期望任何输入时按下键盘上的键。 不是忽略用户的按键,而是将数据放入输入流中,等待程序准备好。
相反,输出流用于保存特定数据使用者的输出,例如监视器、文件或打印机。 将数据写入输出设备时,设备可能尚未准备好接受该数据——例如,当程序将数据写入其输出流时,打印机可能仍在预热。 数据将位于输出流中,直到打印机开始使用它。
一些设备,例如文件和网络,既可以作为输入源,也可以作为输出源。
使用流的好处是程序员只需要学习如何与流交互,就可以在许多不同类型的设备上读取和写入数据。 关于流如何与它们所连接的实际设备交互的细节由环境或操作系统决定。
2、Input/output in C++
虽然 ios 类通常派生自 ios_base,但 ios 通常是您将直接使用的最基类。 ios 类定义了一堆对输入和输出流都通用的东西。
istream 类是处理输入流时使用的主要类。 对于输入流,提取运算符 (>>) 用于从流中删除值。 这是有道理的:当用户按下键盘上的一个键时,键码被放置在一个输入流中。 然后,您的程序从流中提取值并使用。
ostream 类是处理输出流时使用的主要类。 对于输出流,插入运算符 (<<) 用于将值放入流中。 这也很有意义:您将值插入流中,数据使用者(例如监视器)使用它们。
iostream 类可以处理输入和输出,允许双向 I/O。
最后,还有一堆以“_withassign”结尾的类。 这些流类派生自 istream、ostream 和 iostream(分别)并定义了赋值运算符,允许您将一个流分配给另一个流。 在大多数情况下,不会直接处理这些类。
3、C++ 中的标准流
标准流是由其环境提供给计算机程序的预连接流。 C++ 带有四个预定义的标准流对象,它们已经设置好供您使用。
cin -- 一个绑定到标准输入(通常是键盘)的 istream_withassign 类
cout - 一个绑定到标准输出(通常是监视器)的 ostream_withassign 类
cerr -- 与标准错误(通常是监视器)相关的 ostream_withassign 类,提供无缓冲输出
clog - 与标准错误(通常是监视器)相关的 ostream_withassign 类,提供缓冲输出
非缓冲输出通常会立即处理,而缓冲输出通常作为块存储和写出。 因为 clog 不经常使用,它经常从标准流列表中省略。
三、Input with istream
iostream 库相当复杂,我们将向您展示最常用的功能以了解输入类 (istream) 的各个方面。
1、提取运算符
我们可以使用提取运算符 (>>) 从输入流中读取信息。 C++ 为所有内置数据类型预定义了提取操作,并且您已经了解了如何为自己的类重载提取操作符。
读取字符串时,提取运算符的一个常见问题是如何防止输入溢出缓冲区。
给定以下示例:
char buf[10];
std::cin >> buf;
如果用户输入 18 个字符会发生什么? 缓冲区溢出。 一般来说,对用户将输入多少个字符做出任何假设都是一个坏主意。
处理此问题的一种方法是使用操纵器。 操纵器是一个对象,用于在与提取 (>>) 或插入 (setw (在 iomanip.h 标头中) 一起应用时修改流,该对象可用于限制从流中读取的字符数。 使用 setw(),只需提供要读取的最大字符数作为参数,并将其插入到您的输入语句中,如下所示:
#include <iomanip.h>
char buf[10];
std::cin >> std::setw(10) >> buf;
该程序现在只会从流中读取前 9 个字符(为终止符留出空间)。 任何剩余的字符都将留在流中,直到下一次提取。
2、Extraction and whitespace
到目前为止我们没有提到的一件事是提取运算符处理“格式化”数据——也就是说,它跳过了空格(空格、制表符和换行符)。
看看下面的程序:
int main()
{
char ch;
while (std::cin >> ch)
std::cout << ch;
return 0;
}
当用户输入以下内容时:
this is a book
提取运算符跳过空格和换行符。 因此,输出为:
thisisabook
通常,您会想要获取用户输入但不想丢弃空格。 为此,istream 类提供了许多可用于此目的的函数。
其中最有用的是 get() 函数,它只是从输入流中获取一个字符。 这是与上面使用 get() 相同的程序:
int main()
{
char ch;
while (std::cin.get(ch))
std::cout << ch;
return 0;
}
使用这个程序,输出为
this is a book
std::get() 也有一个字符串版本,可以控制读取的数量:
int main()
{
char strBuf[11];
std::cin.get(strBuf, 11);
std::cout << strBuf << '\n';
return 0;
}
请注意,我们只读取前 10 个字符(我们必须留一个字符作为终止符)。 其余字符留在输入流中。
关于 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;
}
有一个特殊版本的 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;
}
3、一些有用的 istream 函数
ignore() 丢弃流中的第一个字符。
ignore(int nCount) 丢弃前 nCount 个字符。
peek() 允许您从流中读取字符而不将其从流中删除。
unget() 将读回的最后一个字符返回到流中,以便下一次调用可以再次读取它。
putback(char ch) 允许您将您选择的字符放回流中以供下一次调用读取。
istream 包含许多其他功能和上述功能的变体,它们可能有用,具体取决于您需要做什么。