C++ IO流详解:标准IO、文件IO与字符串IO实战

C++ IO流详解:标准IO、文件IO与字符串IO实战(2025 最新版,C++20/23 视角)

C++ 的 IO 流是面向对象 + 模板 + 运算符重载的经典设计,所有流都继承自 std::ios_basestd::basic_iosstd::basic_istream / std::basic_ostream

std::istream  ← std::cin / ifstream / istringstream
std::ostream  ← std::cout / ofstream / ostringstream
std::iostream ← fstream / stringstream

三大场景(标准 / 文件 / 字符串)底层完全复用同一套机制,掌握一个就能通吃三个。

一、标准IO()—— 最常用

1. 核心对象

对象类型用途缓冲?刷新时机
cinistream标准输入遇到 \n 或手动
coutostream标准输出满或 std::endl
cerrostream标准错误(无缓冲)立即输出
clogostream标准日志(有缓冲)满或手动

2. 格式化输出(最实用技巧)

#include <iostream>
#include <iomanip>   // setw / setprecision / fixed 等

int main() {
    double pi = 3.1415926535;
    std::cout << std::fixed << std::setprecision(4) << pi << '\n';     // 3.1416
    std::cout << std::setw(10) << std::left << "Hello" << "|\n";       // Hello     |
    std::cout << std::boolalpha << true << '\n';                       // true
}

C++20 推荐:用 std::format(比 << 更清晰):

#include <format>
std::cout << std::format("π = {:.4f}, 姓名: {:<10}\n", pi, "张三");

3. 输入实战(避免最常见 bug)

std::string name;
int age;
std::cin >> name >> age;           // 遇到空格就结束
std::cin.ignore(1000, '\n');       // 清空缓冲区!!!

std::getline(std::cin, name);      // 读整行(推荐)

混合输入神器

while (std::getline(std::cin, line)) {
    std::istringstream iss(line);  // 后面会讲
    int a, b;
    if (iss >> a >> b) { ... }
}

二、文件IO()—— 生产级必备

1. 打开模式(ios:: 组合)

模式组合含义常用场景
ios::in只读读取配置文件
ios::out只写(会清空)新建日志
ios::app追加写入日志文件
ios::binary二进制模式图片/视频/对象
ios::trunc清空(out 默认)重写文件
ios::ate打开后定位到末尾追加前先定位
std::ofstream ofs("log.txt", std::ios::out | std::ios::app);
if (!ofs) { /* 打开失败 */ }

2. 文本文件读写完整示例(推荐现代写法)

#include <fstream>
#include <vector>
#include <string>

bool write_csv(const std::string& filename, const std::vector<std::vector<std::string>>& data) {
    std::ofstream ofs(filename, std::ios::out | std::ios::trunc);
    if (!ofs.is_open()) return false;

    for (const auto& row : data) {
        for (size_t i = 0; i < row.size(); ++i) {
            ofs << row[i];
            if (i + 1 < row.size()) ofs << ',';
        }
        ofs << '\n';
    }
    return true;
}

std::vector<std::string> read_lines(const std::string& filename) {
    std::ifstream ifs(filename);
    if (!ifs.is_open()) return {};

    std::vector<std::string> lines;
    std::string line;
    while (std::getline(ifs, line)) {
        lines.push_back(std::move(line));
    }
    return lines;
}

3. 二进制文件读写(对象序列化)

struct Player {
    int id;
    char name[32];
    float score;
};

void save_player(const Player& p, const std::string& file) {
    std::ofstream ofs(file, std::ios::binary);
    ofs.write(reinterpret_cast<const char*>(&p), sizeof(Player));
}

Player load_player(const std::string& file) {
    Player p{};
    std::ifstream ifs(file, std::ios::binary);
    ifs.read(reinterpret_cast<char*>(&p), sizeof(Player));
    return p;
}

4. 错误处理全家桶

if (fs.fail())        // 逻辑错误(格式不对)
if (fs.eof())         // 到达文件尾
if (fs.bad())         // 严重错误(磁盘坏)
if (!fs.good())       // 任意错误

fs.clear();           // 重置状态
fs.ignore(1000, '\n'); // 跳过一行

三、字符串IO()—— 最被低估的神器

1. 三大类

用途典型场景
istringstream把字符串当作输入流解析 CSV / 命令行
ostringstream把输出拼成字符串拼接日志、JSON、SQL
stringstream可读可写格式转换、临时缓冲

2. 实战1:字符串 → 数字(比 stoi 更灵活)

std::string s = "123 45.67 hello 999";
std::istringstream iss(s);

int a; double b; std::string c; int d;
iss >> a >> b >> c >> d;   // a=123, b=45.67, c="hello", d=999

3. 实战2:数字 → 字符串 + 格式控制(C++20 前最优雅方式)

double price = 1234.56789;
std::ostringstream oss;
oss << std::fixed << std::setprecision(2) << price;
std::string result = oss.str();   // "1234.57"

4. 实战3:CSV 解析器(一行搞定)

std::vector<std::string> split_csv(const std::string& line) {
    std::vector<std::string> fields;
    std::istringstream iss(line);
    std::string field;
    while (std::getline(iss, field, ',')) {
        fields.push_back(field);
    }
    return fields;
}

四、现代C++ IO最佳实践(2025 推荐)

  1. RAII + 异常:文件流构造函数失败时默认不抛异常,用 exceptions() 开启
   std::ofstream ofs;
   ofs.exceptions(std::ios::failbit | std::ios::badbit);
   ofs.open("data.txt");
  1. C++17 std::filesystem + 流
   namespace fs = std::filesystem;
   if (fs::exists("config.json")) { ... }
  1. C++20 std::format + stringstream 组合拳最强
  2. 避免全局 using namespace std;
  3. 大文件读写:用 std::ifstream::read() + std::string::reserve() 预分配

五、常见坑 & 一句话解决

问题原因一句话解决
cin >>getline缓冲区残留 \nstd::cin.ignore(1000, '\n')
文件没写进去没调用 close() 或没刷新std::endlofs.flush()
二进制文件乱码用了文本模式加上 std::ios::binary
性能差(GB 级文件)一字节一字节读read() + 大缓冲

一句话总结

  • 标准IO → 日常交互
  • 文件IO → 持久化
  • 字符串IO → 数据转换与解析

掌握这三者,你就真正掌握了 C++ 的“输入输出灵魂”。

需要下面任意一个完整可编译项目模板,直接告诉我:

  • 日志系统(多线程安全 + 分文件)
  • CSV 读写库(带表头、类型转换)
  • 二进制对象持久化(支持 vector/map)
  • 配置解析器(ini / json 轻量版)
  • 大文件拷贝 + 进度条

我直接给你带 CMakeLists.txt 的完整代码。

文章已创建 4812

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部