一、什么是“输出流”?
输出流(Output Stream) 是 C++ 中用于 将数据从程序发送到外部设备 的抽象机制。
数据像“水流”一样,从程序流向:屏幕、文件、网络、字符串等。
二、输出流家族总览
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
| 类 | 用途 | 继承关系 |
|---|
std::ostream | 通用输出流基类 | std::ios → std::ostream |
std::ofstream | 文件输出流 | std::ostream |
std::ostringstream | 字符串输出流 | std::ostream |
std::cout | 标准输出(屏幕) | std::ostream |
std::cerr | 标准错误(屏幕,不缓冲) | std::ostream |
std::clog | 标准日志(屏幕,有缓冲) | std::ostream |
三、核心操作:<< 插入运算符
std::cout << "Hello" << 42 << 3.14 << '\n';
工作原理:
- 自动类型转换
- 链式调用(返回
*this) - 缓冲机制(默认不立即刷新)
std::cout << "Loading" << std::flush;
std::cout << "Loading" << std::endl;
四、标准输出流对比
| 流 | 用途 | 缓冲 | 典型场景 |
|---|
std::cout | 正常输出 | 有缓冲 | 程序结果 |
std::cerr | 错误信息 | 无缓冲 | 调试、错误 |
std::clog | 日志信息 | 有缓冲 | 运行日志 |
std::cout << "Info: Starting...\n";
std::cerr << "ERROR: File not found!\n";
std::clog << "LOG: User logged in\n";
五、输出流的成员函数
| 函数 | 功能 | 示例 |
|---|
operator<< | 插入数据 | cout << x; |
put() | 输出单个字符 | cout.put('A'); |
write() | 输出原始字节 | cout.write(buf, n); |
flush() | 强制刷新缓冲区 | cout.flush(); |
seekp() | 设置写位置(文件流) | file.seekp(0); |
tellp() | 获取当前写位置 | auto pos = file.tellp(); |
六、格式控制(<iomanip>)—— 核心!
| 操纵符 | 作用 | 示例 |
|---|
std::endl | 换行 + 刷新 | cout << endl; |
std::flush | 强制刷新 | cout << flush; |
std::setw(n) | 设置字段宽度 | cout << setw(10) << x; |
std::setfill(c) | 填充字符 | cout << setfill('0'); |
std::left / std::right / std::internal | 对齐方式 | cout << left; |
std::hex / std::dec / std::oct | 进制 | cout << hex << 255; → ff |
std::showbase | 显示进制前缀 | 0xff, 077 |
std::boolalpha | true/false | cout << boolalpha << true; |
std::fixed | 固定小数点 | 3.14 |
std::scientific | 科学计数法 | 3.140000e+00 |
std::setprecision(n) | 精度 | setprecision(2) |
示例:格式化表格
#include <iostream>
#include <iomanip>
using namespace std;
cout << left << setw(15) << "Name"
<< right << setw(10) << "Age"
<< setw(12) << "Salary" << endl;
cout << setfill('-') << setw(37) << "" << setfill(' ') << endl;
cout << left << setw(15) << "Alice"
<< right << setw(10) << 25
<< setw(12) << fixed << setprecision(2) << 50000.50 << endl;
输出:
Name Age Salary
-------------------------------------
Alice 25 50000.50
七、输出到文件
std::ofstream file("output.txt");
if (!file.is_open()) {
std::cerr << "无法打开文件!\n";
return 1;
}
file << "Hello, 文件!" << 42 << '\n';
std::ofstream file2("log.txt", std::ios::app);
file2 << "新日志\n";
模式标志
| 模式 | 含义 |
|---|
std::ios::out | 写入(默认) |
std::ios::app | 追加 |
std::ios::trunc | 清空文件 |
std::ios::binary | 二进制模式 |
八、字符串输出流:std::ostringstream
#include <sstream>
std::ostringstream oss;
oss << "Score: " << 95 << ", Rank: " << 1;
std::string result = oss.str();
典型用途:
std::string json = [&]{
std::ostringstream oss;
oss << "{ \"name\": \"" << name << "\", \"age\": " << age << " }";
return oss.str();
}();
九、自定义输出(重载 <<)
struct Point {
int x, y;
};
std::ostream& operator<<(std::ostream& os, const Point& p) {
os << "(" << p.x << ", " << p.y << ")";
return os;
}
Point p{10, 20};
std::cout << p << "\n";
十、底层机制:streambuf
std::cout.rdbuf()
std::cout.rdbuf(new_buf)
应用:重定向输出
std::ofstream file("log.txt");
std::streambuf* old_buf = std::cout.rdbuf(file.rdbuf());
std::cout << "这会写入文件!\n";
std::cout.rdbuf(old_buf);
十一、性能优化技巧
| 技巧 | 说明 |
|---|
std::ios::sync_with_stdio(false); | 关闭 C 流同步,提速 5~10 倍 |
cout.tie(nullptr); | 解绑 cin 和 cout |
减少 endl | 用 '\n' 替代(endl 会 flush) |
| 批量写入 | 用 write() 写大块数据 |
int main() {
std::ios::sync_with_stdio(false);
cout.tie(nullptr);
for (int i = 0; i < 1000000; ++i) {
cout << i << '\n';
}
}
十二、缓冲机制详解
| 缓冲类型 | 触发刷新条件 |
|---|
| 全缓冲(文件) | 缓冲区满 |
行缓冲(cout) | 遇到 \n |
无缓冲(cerr) | 立即输出 |
std::cout << "A";
std::cout << "B\n";
std::cout << std::flush;
十三、异常机制(可选)
std::cout.exceptions(std::ios::failbit | std::ios::badbit);
try {
std::cout << "写入...";
} catch (const std::ios::failure& e) {
std::cerr << "写入失败: " << e.what() << "\n";
}
十四、完整示例:日志系统
class Logger {
std::ostringstream buffer;
std::ofstream file;
public:
Logger(const std::string& filename) : file(filename, std::ios::app) {}
template<typename T>
Logger& operator<<(const T& data) {
buffer << data;
return *this;
}
Logger& operator<<(std::ostream& (*manip)(std::ostream&)) {
if (manip == static_cast<std::ostream& (*)(std::ostream&)>(std::endl)) {
std::string line = buffer.str();
std::cout << "[LOG] " << line << std::endl;
if (file) file << line << std::endl;
buffer.str("");
}
return *this;
}
};
Logger log("app.log");
log << "User " << username << " logged in at " << time << endl;
十五、常见错误与防御性编程
| 错误 | 后果 | 正确做法 |
|---|
cout << endl 频繁使用 | 性能低下 | 用 '\n' |
| 文件未关闭 | 资源泄露 | RAII(ofstream 自动关闭) |
忘记 flush | 输出延迟 | 关键日志用 cerr 或 flush |
| 忽略返回值 | 写入失败未检测 | 检查 if (!file) |
十六、总结:输出流核心要点
| 要点 | 内容 |
|---|
| 核心操作 | <<(插入)、endl、flush |
| 格式控制 | setw, setprecision, hex, fixed 等 |
| 三大标准流 | cout(正常)、cerr(错误)、clog(日志) |
| 性能优化 | sync_with_stdio(false) + '\n' |
| 高级用法 | ostringstream 拼接、rdbuf() 重定向 |
| 自定义 | 重载 << 支持结构体 |
推荐学习资源
一句话总结:
输出流是 C++ 与外界沟通的桥梁,掌握 <<、iomanip、ostringstream 就能写出优雅、可读、高效的输出代码。