Skip to main content
Version: Next

iostream

iostream 是 C++ 标准库中最常用的输入输出流库,负责把程序中的数据与外部设备连接起来,例如键盘、终端、文件、字符串缓冲区等。它的设计核心是“流”:数据像水流一样从一个地方流向另一个地方,输入流把数据读进程序,输出流把数据写出程序。

和 C 语言中的 printfscanf 相比,iostream 更强调类型安全、可扩展性和面向对象设计。它可以直接处理 C++ 类型,也允许用户为自己的类重载输入输出运算符,从而让自定义对象像内置类型一样被读写。

头文件与常用对象

使用标准输入输出流时,通常包含头文件:

#include <iostream>

这个头文件提供了四个最常见的全局流对象:

对象类型作用
std::cinstd::istream标准输入,通常对应键盘
std::coutstd::ostream标准输出,通常对应终端
std::cerrstd::ostream标准错误输出,通常不缓冲
std::clogstd::ostream标准日志输出,通常缓冲

最基本的例子:

#include <iostream>

int main() {
int age;

std::cout << "请输入年龄: ";
std::cin >> age;

std::cout << "你的年龄是 " << age << '\n';
return 0;
}

其中 << 是输出运算符,表示把右侧数据写入左侧输出流;>> 是输入运算符,表示从左侧输入流读取数据到右侧变量。

流库的基本结构

iostream 并不是一个孤立的类,而是一组围绕输入输出构建的类型体系。常见类型包括:

类型说明
std::ios输入输出流的公共基础类,管理格式、状态等
std::istream输入流基类,支持 >>getgetline 等读取操作
std::ostream输出流基类,支持 <<putwrite 等写入操作
std::iostream同时支持输入和输出
std::ifstream文件输入流,定义在 <fstream>
std::ofstream文件输出流,定义在 <fstream>
std::fstream文件输入输出流,定义在 <fstream>
std::istringstream字符串输入流,定义在 <sstream>
std::ostringstream字符串输出流,定义在 <sstream>
std::stringstream字符串输入输出流,定义在 <sstream>

可以把它理解成一套统一接口:无论数据来自键盘、文件还是字符串,都可以通过相似的方式读取;无论数据写到终端、文件还是字符串,也都可以通过相似的方式输出。

格式化输出

流对象可以控制数字、宽度、填充字符、进制、精度等格式。常用格式控制符定义在 <iomanip> 中。

#include <iomanip>
#include <iostream>

int main() {
double value = 3.1415926;
int number = 255;

std::cout << std::fixed << std::setprecision(2) << value << '\n';
std::cout << std::hex << number << '\n';
std::cout << std::setw(8) << std::setfill('*') << 42 << '\n';

return 0;
}

常用格式控制符:

控制符作用
std::endl输出换行并刷新缓冲区
std::flush立即刷新缓冲区
std::setw(n)设置下一个输出字段宽度
std::setfill(c)设置填充字符
std::setprecision(n)设置浮点数精度
std::fixed使用定点小数格式
std::scientific使用科学计数法
std::hex使用十六进制
std::dec使用十进制
std::oct使用八进制
std::boolalpha把布尔值输出为 true / false

需要注意的是,有些格式状态会保留在流对象中,例如 std::fixedstd::hex;有些只影响下一次输出,例如 std::setw

输入方式

std::cin >> value 是最常见的输入方式,它会跳过前导空白字符,并按目标变量的类型解析数据:

int x;
double y;
std::string name;

std::cin >> x >> y >> name;

这种方式适合读取用空格、换行、制表符分隔的数据,但它不会读取包含空格的一整行文本。如果需要读取整行,应使用 std::getline

#include <iostream>
#include <string>

int main() {
std::string line;

std::getline(std::cin, line);
std::cout << "输入内容: " << line << '\n';

return 0;
}

一个常见坑是混用 >>std::getline>> 读取数字后会把换行符留在输入缓冲区,下一次 getline 可能会直接读到空行:

int age;
std::string name;

std::cin >> age;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::getline(std::cin, name);

上面这段代码需要包含:

#include <limits>

流状态

输入输出操作可能失败,因此每个流对象都会维护自己的状态。常见状态包括:

状态说明
good()流状态正常
eof()到达文件或输入末尾
fail()格式解析失败,通常可以恢复
bad()严重错误,通常不可恢复

例如读取整数时,如果用户输入了非数字内容,std::cin 会进入失败状态:

#include <iostream>
#include <limits>

int main() {
int value;

while (!(std::cin >> value)) {
std::cout << "请输入一个整数: ";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

std::cout << "读取成功: " << value << '\n';
return 0;
}

clear() 用来清除错误状态,ignore() 用来丢弃缓冲区中的错误输入。

文件流

虽然 iostream 主要指标准输入输出流,但它的思想也延伸到了文件流。文件流定义在 <fstream> 中。

#include <fstream>
#include <iostream>
#include <string>

int main() {
std::ofstream out("data.txt");
out << "hello file\n";

std::ifstream in("data.txt");
std::string line;

while (std::getline(in, line)) {
std::cout << line << '\n';
}

return 0;
}

文件流的优势是接口和 cincout 非常相似。把输出目标从终端换成文件,通常只需要把 std::cout 换成 std::ofstream 对象。

字符串流

字符串流定义在 <sstream> 中,可以把字符串当作输入输出设备使用,常用于解析文本、拼接格式化内容、类型转换等。

#include <iostream>
#include <sstream>
#include <string>

int main() {
std::string text = "Tom 18 92.5";
std::istringstream input(text);

std::string name;
int age;
double score;

input >> name >> age >> score;

std::cout << name << ", " << age << ", " << score << '\n';
return 0;
}

构造输出字符串:

#include <sstream>
#include <string>

std::string make_message(const std::string& name, int score) {
std::ostringstream output;
output << name << " 的分数是 " << score;
return output.str();
}

自定义类型的输入输出

iostream 的一个重要特点是可以通过重载运算符支持自定义类型。

#include <iostream>
#include <string>

struct Person {
std::string name;
int age;
};

std::ostream& operator<<(std::ostream& os, const Person& person) {
os << person.name << " (" << person.age << ")";
return os;
}

std::istream& operator>>(std::istream& is, Person& person) {
is >> person.name >> person.age;
return is;
}

int main() {
Person p;

std::cin >> p;
std::cout << p << '\n';

return 0;
}

重载时通常要返回流对象引用,这样才能支持链式调用:

std::cout << a << b << c;

缓冲区与刷新

输出流通常带有缓冲区。程序先把内容写入缓冲区,再在合适的时机真正输出。这样可以减少频繁 IO 带来的性能开销。

常见刷新方式:

写法行为
'\n'输出换行,不一定立即刷新
std::endl输出换行并刷新
std::flush只刷新,不输出换行

如果只是换行,通常优先使用 '\n',因为 std::endl 会额外刷新缓冲区,频繁使用可能降低性能。

性能优化

在算法竞赛或大量输入输出场景中,常见写法是:

std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);

sync_with_stdio(false) 会取消 C++ 流与 C 标准 IO 的同步,从而提高速度;cin.tie(nullptr) 会解除 cincout 的绑定,避免每次输入前自动刷新输出。

完整示例:

#include <iostream>

int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);

int n;
std::cin >> n;

long long sum = 0;
for (int i = 0; i < n; ++i) {
int x;
std::cin >> x;
sum += x;
}

std::cout << sum << '\n';
return 0;
}

使用这两行后,不建议再混用 scanfprintfgetcharputs 等 C 风格 IO,否则可能出现输入输出顺序不符合预期的问题。

常见使用建议

  1. 普通换行优先使用 '\n',需要立即刷新时再使用 std::endl
  2. 读取整行文本时使用 std::getline,读取空白分隔的数据时使用 >>
  3. 混用 >>getline 时,注意处理残留换行符。
  4. 判断输入是否成功时,可以直接把流对象放在条件中,例如 while (std::cin >> x)
  5. 大量输入输出时,可以关闭同步并解除绑定来提升性能。
  6. 自定义类型推荐重载 operator<<,这样调试和日志输出会更自然。
  7. 需要解析字符串时,优先考虑 std::istringstream,不要手写复杂的字符扫描逻辑。

小结

iostream 是 C++ IO 系统的基础。它提供了统一、类型安全、可扩展的输入输出方式,既能处理终端交互,也能扩展到文件和字符串。掌握 cincout、格式控制、流状态、文件流、字符串流和自定义类型重载,基本就能覆盖日常 C++ 开发中大多数输入输出需求。