一、什么是"输出流"?
输出流(Output Stream) 是 C++ 中用于 将数据从程序发送到外部设备 的抽象机制。
数据像"水流"一样,从程序流向:屏幕、文件、网络、字符串等。
二、输出流家族总览
cpp
复制代码
#include <iostream> // std::cout, std::cerr, std::clog
#include <fstream> // std::ofstream
#include <sstream> // std::ostringstream
#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 |
三、核心操作:<< 插入运算符
cpp
复制代码
std::cout << "Hello" << 42 << 3.14 << '\n';
工作原理:
- 自动类型转换
- 链式调用 (返回
*this)
- 缓冲机制(默认不立即刷新)
cpp
复制代码
std::cout << "Loading" << std::flush; // 强制刷新
std::cout << "Loading" << std::endl; // 换行 + 刷新
四、标准输出流对比
| 流 |
用途 |
缓冲 |
典型场景 |
std::cout |
正常输出 |
有缓冲 |
程序结果 |
std::cerr |
错误信息 |
无缓冲 |
调试、错误 |
std::clog |
日志信息 |
有缓冲 |
运行日志 |
cpp
复制代码
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) |
示例:格式化表格
cpp
复制代码
#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
七、输出到文件
cpp
复制代码
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
cpp
复制代码
#include <sstream>
std::ostringstream oss;
oss << "Score: " << 95 << ", Rank: " << 1;
std::string result = oss.str(); // "Score: 95, Rank: 1"
典型用途:
cpp
复制代码
std::string json = [&]{
std::ostringstream oss;
oss << "{ \"name\": \"" << name << "\", \"age\": " << age << " }";
return oss.str();
}();
九、自定义输出(重载 <<)
cpp
复制代码
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"; // (10, 20)
十、底层机制:streambuf
cpp
复制代码
std::cout.rdbuf() // 获取缓冲区
std::cout.rdbuf(new_buf) // 替换缓冲区
应用:重定向输出
cpp
复制代码
// 将 cout 重定向到文件
std::ofstream file("log.txt");
std::streambuf* old_buf = std::cout.rdbuf(file.rdbuf());
// 现在 cout 写入文件
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() 写大块数据 |
cpp
复制代码
int main() {
std::ios::sync_with_stdio(false);
cout.tie(nullptr);
for (int i = 0; i < 1000000; ++i) {
cout << i << '\n'; // 比 endl 快得多
}
}
十二、缓冲机制详解
| 缓冲类型 |
触发刷新条件 |
| 全缓冲(文件) |
缓冲区满 |
行缓冲 (cout) |
遇到 \n |
无缓冲 (cerr) |
立即输出 |
cpp
复制代码
std::cout << "A"; // 还在缓冲区
std::cout << "B\n"; // 遇到 \n,刷新 → 输出 "AB"
std::cout << std::flush; // 强制刷新
十三、异常机制(可选)
cpp
复制代码
std::cout.exceptions(std::ios::failbit | std::ios::badbit);
try {
std::cout << "写入...";
} catch (const std::ios::failure& e) {
std::cerr << "写入失败: " << e.what() << "\n";
}
十四、完整示例:日志系统
cpp
复制代码
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 就能写出优雅、可读、高效的输出代码。