首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >《C++ 程序设计》第 11 章 - 流类库与输入输出

《C++ 程序设计》第 11 章 - 流类库与输入输出

作者头像
啊阿狸不会拉杆
发布2026-01-21 12:41:27
发布2026-01-21 12:41:27
100
举报

引言

        在 C++ 编程中,输入输出(I/O)是程序与外部世界交互的桥梁。无论是从键盘读取数据、向屏幕输出结果,还是读写文件、处理字符串,都离不开 C++ 标准库提供的流类库。本章将全面解析 C++ 流类库的结构与使用,从基础的控制台 I/O 到文件操作、字符串流,再到高级的对象串行化,带你掌握 C++ 中所有 I/O 操作的核心技能。

11.1 I/O 流的概念及流类库结构

1.1.1 流的基本概念

        "流"(Stream)是 C++ 中对数据传输的抽象概念。想象水流从一个地方流向另一个地方,数据也像水流一样在程序与外部设备(键盘、屏幕、文件等)之间传输:

  • 输入流:数据从外部设备流向程序(如从键盘读入数据)
  • 输出流:数据从程序流向外部设备(如向屏幕输出结果)
  • 流状态:流在传输过程中会有状态(正常、出错、文件结束等),可通过流的状态标志判断操作是否成功
1.1.2 流类库结构

C++ 流类库以类层次结构实现,核心类定义在<iostream><fstream><sstream>等头文件中。

流类库核心类图(PlantUML)
代码语言:javascript
复制
@startuml
class ios {
  + enum openmode { in, out, app, ate, trunc, binary }
  + enum seekdir { beg, cur, end }
  + bool good()
  + bool eof()
  + bool fail()
  + bool bad()
  + void clear()
  + operator bool()
}

class istream {
  + istream& operator>>(T& val)
  + int get()
  + istream& get(char& c)
  + istream& getline(char* buf, int count)
  + istream& read(char* buf, streamsize count)
  + streampos tellg()
  + istream& seekg(streampos pos)
}

class ostream {
  + ostream& operator<<(T val)
  + ostream& put(char c)
  + ostream& write(const char* buf, streamsize count)
  + streampos tellp()
  + ostream& seekp(streampos pos)
  + ostream& flush()
}

class iostream {
  // 继承istream和ostream的功能
}

class ifstream {
  + ifstream()
  + ifstream(const char* filename, openmode mode = in)
  + bool is_open()
  + void open(const char* filename, openmode mode = in)
  + void close()
}

class ofstream {
  + ofstream()
  + ofstream(const char* filename, openmode mode = out)
  + bool is_open()
  + void open(const char* filename, openmode mode = out)
  + void close()
}

class fstream {
  + fstream()
  + fstream(const char* filename, openmode mode = in|out)
  + bool is_open()
  + void open(const char* filename, openmode mode = in|out)
  + void close()
}

class istringstream {
  + istringstream()
  + istringstream(const string& str)
  + string str() const
}

class ostringstream {
  + ostringstream()
  + string str() const
  + void str(const string& s)
}

class stringstream {
  // 继承istringstream和ostringstream的功能
}

ios <|-- istream
ios <|-- ostream
istream <|-- iostream
ostream <|-- iostream
istream <|-- ifstream
ostream <|-- ofstream
iostream <|-- fstream
istream <|-- istringstream
ostream <|-- ostringstream
iostream <|-- stringstream
@enduml
流类库知识思维导图

11.2 输出流

        输出流用于将程序中的数据传输到外部设备(屏幕、文件等),C++ 中最常用的输出流是cout(控制台输出)、ofstream(文件输出)和ostringstream(字符串输出)。

11.2.1 构造输出流对象

        输出流对象的构造主要涉及控制台输出流、文件输出流的初始化。

代码示例:构造输出流对象
代码语言:javascript
复制
#include <iostream>
#include <fstream>  // 文件流头文件

using namespace std;

int main() {
    // 1. 控制台输出流(全局对象,无需手动构造)
    cout << "这是控制台输出流" << endl;

    // 2. 文件输出流构造方式1:默认构造+open()
    ofstream ofs1;
    // 打开文件(若文件不存在则创建,存在则清空内容)
    ofs1.open("output1.txt", ios::out);  // ios::out是默认模式,可省略
    if (ofs1.is_open()) {  // 检查文件是否成功打开
        ofs1 << "用ofs1写入的内容" << endl;
        ofs1.close();  // 操作完成后关闭文件
    } else {
        cerr << "ofs1打开文件失败!" << endl;
    }

    // 3. 文件输出流构造方式2:构造函数直接打开
    ofstream ofs2("output2.txt", ios::out | ios::app);  // 追加模式
    if (ofs2) {  // 流对象可直接作为布尔值判断是否正常
        ofs2 << "用ofs2追加的内容" << endl;
        ofs2.close();
    } else {
        cerr << "ofs2打开文件失败!" << endl;
    }

    return 0;
}
11.2.2 使用插入运算符和操纵符

   <<是输出流的插入运算符,用于输出数据;操纵符(Manipulator)用于控制输出格式(如对齐、精度等)。

常用输出操纵符

操纵符

功能

头文件

endl

换行并刷新缓冲区

<iostream>

ends

输出空字符\0

<iostream>

flush

刷新缓冲区

<iostream>

setw(n)

设置输出宽度为 n

<iomanip>

setfill(c)

设置填充字符为 c

<iomanip>

setprecision(n)

设置浮点数精度

<iomanip>

fixed

固定小数位格式

<iomanip>

left/right/internal

左对齐 / 右对齐 / 内部对齐

<iomanip>

hex/oct/dec

十六进制 / 八进制 / 十进制输出

<iostream>

代码示例:插入运算符与操纵符使用
代码语言:javascript
复制
#include <iostream>
#include <iomanip>  // 格式操纵符头文件

using namespace std;

int main() {
    // 基本数据输出
    int a = 100;
    double b = 3.1415926535;
    char c = 'A';
    string s = "Hello C++";

    cout << "整数:" << a << endl;
    cout << "浮点数:" << b << endl;
    cout << "字符:" << c << endl;
    cout << "字符串:" << s << endl << endl;

    // 格式控制示例
    cout << "===== 格式控制演示 =====" << endl;
    
    // 宽度与填充
    cout << "默认宽度:" << a << endl;
    cout << "设置宽度10:" << setw(10) << a << endl;  // 右对齐,宽度不足用空格填充
    cout << "左对齐+填充:" << setw(10) << left << setfill('*') << a << endl;  // 左对齐,用*填充
    cout << resetiosflags(ios::left);  // 重置对齐方式(恢复默认)

    // 浮点数精度控制
    cout << "\n浮点数精度演示:" << endl;
    cout << "默认精度:" << b << endl;  // 默认6位有效数字
    cout << "setprecision(8):" << setprecision(8) << b << endl;  // 8位有效数字
    cout << "fixed+precision(4):" << fixed << setprecision(4) << b << endl;  // 固定4位小数

    // 进制转换
    cout << "\n进制转换演示:" << endl;
    cout << "十进制:" << dec << a << endl;  // 十进制(默认)
    cout << "八进制:" << oct << a << endl;  // 八进制
    cout << "十六进制:" << hex << a << endl;  // 十六进制(小写)
    cout << "十六进制大写:" << uppercase << hex << a << endl;  // 十六进制大写

    return 0;
}
11.2.3 文件输出流成员函数

除了<<运算符,输出流还提供了put()write()等成员函数用于输出操作。

常用输出流成员函数

函数

功能

put(char c)

输出单个字符

write(const char* buf, streamsize n)

输出缓冲区 buf 中的 n 个字节

tellp()

返回当前写位置

seekp(pos)

设置写位置

flush()

刷新输出缓冲区

close()

关闭文件

代码示例:文件输出流成员函数使用
代码语言:javascript
复制
#include <iostream>
#include <fstream>
#include <cstring>  // 用于strlen

using namespace std;

int main() {
    ofstream ofs("member_func.txt");
    if (!ofs) {
        cerr << "文件打开失败!" << endl;
        return 1;
    }

    // put():输出单个字符
    ofs.put('H');
    ofs.put('i');
    ofs.put('\n');  // 换行

    // write():输出字节序列(适合二进制数据)
    const char* str = "使用write()写入的字符串";
    // 注意:write()不会自动添加结束符,需手动控制长度
    ofs.write(str, strlen(str));
    ofs.put('\n');

    // tellp():获取当前写位置
    streampos pos = ofs.tellp();
    cout << "当前写位置:" << pos << endl;

    // 写入一些内容
    ofs << "这是一段测试文本" << endl;

    // seekp():移动写位置到开头
    ofs.seekp(0, ios::beg);  // ios::beg表示从文件开头计算
    ofs << "替换开头内容";  // 覆盖开头部分

    ofs.close();  // 关闭文件
    cout << "操作完成,请查看member_func.txt" << endl;

    return 0;
}
11.2.4 二进制输出文件

        二进制文件直接存储数据的内存表示,适合存储结构化数据(如结构体)。与文本文件的区别是:二进制文件读写需指定ios::binary模式,且通常使用write()/read()函数。

代码示例:二进制输出文件操作
代码语言:javascript
复制
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

// 定义一个结构体表示学生信息
struct Student {
    int id;         // 学号
    char name[20];  // 姓名(固定长度,避免字符串指针问题)
    float score;    // 成绩
};

int main() {
    // 1. 创建学生数据
    Student s1 = {1001, "张三", 95.5f};
    Student s2 = {1002, "李四", 88.0f};
    Student s3 = {1003, "王五", 92.3f};

    // 2. 二进制模式打开文件(ios::binary)
    ofstream ofs("students.dat", ios::out | ios::binary);
    if (!ofs) {
        cerr << "文件打开失败!" << endl;
        return 1;
    }

    // 3. 用write()写入二进制数据
    // write()参数:数据地址(需强转为char*)、数据大小
    ofs.write(reinterpret_cast<const char*>(&s1), sizeof(s1));
    ofs.write(reinterpret_cast<const char*>(&s2), sizeof(s2));
    ofs.write(reinterpret_cast<const char*>(&s3), sizeof(s3));

    ofs.close();
    cout << "二进制文件写入完成!" << endl;

    return 0;
}
11.2.5 字符串输出流

   ostringstream用于将数据写入字符串缓冲区,可实现 "字符串拼接" 或 "数据格式化转字符串" 功能,定义在<sstream>头文件中。

代码示例:字符串输出流使用
代码语言:javascript
复制
#include <iostream>
#include <sstream>  // 字符串流头文件
#include <string>
#include <iomanip>  // 包含setprecision等格式化操纵符的头文件

using namespace std;

int main() {
    // 1. 基本使用:拼接字符串
    ostringstream oss1;
    oss1 << "姓名:" << "张三" 
         << ",年龄:" << 20 
         << ",成绩:" << 95.5;
    string info = oss1.str();  // 通过str()获取字符串
    cout << "拼接结果:" << info << endl;

    // 2. 格式化数字转字符串
    int num = 123;
    double pi = 3.1415926;
    
    ostringstream oss2;
    oss2 << "整数转字符串:" << num << endl
         << "浮点数保留2位小数:" << fixed << setprecision(2) << pi;
    cout << "\n格式化结果:\n" << oss2.str() << endl;

    // 3. 清空字符串流(两种方式)
    oss2.str("");  // 方式1:重置内部字符串
    oss2.clear();  // 重置流状态(重要!否则之前的错误状态会保留)
    
    oss2 << "重置后:" << hex << num;  // 十六进制输出123
    cout << "\n重置后结果:" << oss2.str() << endl;

    return 0;
}

11.3 输入流

      输入流用于将外部数据传输到程序中,C++ 中常用的输入流有cin(控制台输入)、ifstream(文件输入)和istringstream(字符串输入)。

11.3.1 构造输入流对象

     输入流对象的构造与输出流类似,重点是文件打开模式和状态检查。

代码示例:构造输入流对象
代码语言:javascript
复制
#include <iostream>
#include <fstream>

using namespace std;

int main() {
    // 1. 控制台输入流(全局对象cin)
    int num;
    cout << "请输入一个整数:";
    cin >> num;
    cout << "你输入的是:" << num << endl;

    // 2. 文件输入流构造方式1:默认构造+open()
    ifstream ifs1;
    ifs1.open("input1.txt");  // 默认模式ios::in
    if (ifs1.is_open()) {
        string line;
        getline(ifs1, line);  // 读取一行
        cout << "ifs1读取内容:" << line << endl;
        ifs1.close();
    } else {
        cerr << "ifs1打开文件失败!" << endl;
    }

    // 3. 文件输入流构造方式2:构造函数直接打开
    ifstream ifs2("input2.txt", ios::in | ios::binary);  // 二进制读模式
    if (ifs2) {  // 检查流状态
        char buf[1024];
        ifs2.read(buf, sizeof(buf)-1);  // 留一个位置存结束符
        buf[ifs2.gcount()] = '\0';  // gcount()返回实际读取的字节数
        cout << "ifs2读取字节数:" << ifs2.gcount() << endl;
        cout << "ifs2读取内容:" << buf << endl;
        ifs2.close();
    } else {
        cerr << "ifs2打开文件失败!" << endl;
    }

    return 0;
}
11.3.2 使用提取运算符

   >>是输入流的提取运算符,用于从输入流中读取数据,默认会跳过空白字符(空格、换行、制表符等)。

代码示例:提取运算符使用
代码语言:javascript
复制
#include <iostream>
#include <string>

using namespace std;

int main() {
    cout << "请输入姓名、年龄、身高(用空格分隔):";
    
    string name;
    int age;
    double height;

    // 提取运算符会自动跳过空白字符
    cin >> name >> age >> height;

    // 输出读取结果
    cout << "\n你输入的信息:" << endl;
    cout << "姓名:" << name << endl;
    cout << "年龄:" << age << endl;
    cout << "身高:" << height << "m" << endl;

    // 注意:提取运算符遇到空白字符停止,适合读取结构化数据
    // 如需读取含空格的字符串,需用getline()
    cout << "\n请输入一句话(含空格):";
    cin.ignore();  // 忽略之前输入留下的换行符
    string sentence;
    getline(cin, sentence);  // 读取一行(含空格)
    cout << "你输入的句子:" << sentence << endl;

    return 0;
}
11.3.3 输入流操纵符

输入流操纵符用于控制输入格式,常用操纵符如下:

操纵符

功能

头文件

skipws

跳过空白字符(默认)

<iostream>

noskipws

不跳过空白字符

<iostream>

hex/oct/dec

设置整数输入进制

<iostream>

代码示例:输入流操纵符使用
代码语言:javascript
复制
#include <iostream>
#include <iomanip>

using namespace std;

int main() {
    // 1. 进制转换输入
    int num;
    cout << "请输入一个十六进制数(如1A):";
    cin >> hex >> num;  // hex设置输入为十六进制
    cout << "转换为十进制:" << dec << num << endl;  // dec恢复十进制输出

    // 2. noskipws:不跳过空白字符
    char c1, c2;
    cout << "\n请输入两个字符(用空格分隔):";
    cin >> skipws >> c1;  // 默认跳过空格
    cin >> noskipws >> c2;  // 不跳过空格(会读取空格)
    cout << "c1:'" << c1 << "',ASCII:" << int(c1) << endl;
    cout << "c2:'" << c2 << "',ASCII:" << int(c2) << endl;

    // 3. 重置为默认状态
    cin >> skipws;
    return 0;
}
11.3.4 输入流相关函数

    输入流提供了get()getline()read()等成员函数,用于更灵活的输入操作。

常用输入流成员函数

函数

功能

get(char& c)

读取单个字符(包括空白)

getline(char* buf, int n)

读取一行到 buf,最多 n-1 个字符

read(char* buf, streamsize n)

读取 n 个字节到 buf

gcount()

返回上次读取的字节数

tellg()

返回当前读位置

seekg(pos)

设置读位置

eof()

判断是否到达文件末尾

fail()

判断是否发生非致命错误

代码示例:输入流成员函数使用
代码语言:javascript
复制
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main() {
    // 1. 控制台输入函数演示
    cout << "请输入一个字符:";
    char c;
    cin.get(c);  // 读取单个字符(包括空格、换行)
    cout << "你输入的字符:'" << c << "',ASCII:" << int(c) << endl;

    cin.ignore();  // 忽略残留的换行符
    cout << "请输入一行话:";
    char buf[100];
    cin.getline(buf, 100);  // 读取一行到缓冲区
    cout << "你输入的内容:" << buf << endl;

    // 2. 文件输入函数演示
    ifstream ifs("input_func.txt");
    if (!ifs) {
        cerr << "文件打开失败!" << endl;
        return 1;
    }

    // 读取文件内容
    char file_buf[1024];
    ifs.read(file_buf, sizeof(file_buf)-1);  // 读取字节
    streamsize bytes_read = ifs.gcount();  // 获取实际读取的字节数
    file_buf[bytes_read] = '\0';  // 添加结束符
    cout << "\n文件读取字节数:" << bytes_read << endl;
    cout << "文件内容:\n" << file_buf << endl;

    // 获取文件大小(移动到末尾再获取位置)
    ifs.seekg(0, ios::end);  // 移动到文件末尾
    streampos file_size = ifs.tellg();  // 获取当前位置(即文件大小)
    cout << "文件总大小:" << file_size << "字节" << endl;

    ifs.close();
    return 0;
}
11.3.5 字符串输入流

   istringstream用于从字符串中读取数据,可实现 "字符串解析" 功能,定义在<sstream>头文件中。

代码示例:字符串输入流使用
代码语言:javascript
复制
#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int main() {
    // 1. 解析结构化字符串
    string data = "张三 20 95.5 男";
    istringstream iss(data);  // 用字符串初始化输入流

    string name;
    int age;
    double score;
    string gender;

    // 从字符串流中提取数据,类似cin
    iss >> name >> age >> score >> gender;
    cout << "解析结果:" << endl;
    cout << "姓名:" << name << endl;
    cout << "年龄:" << age << endl;
    cout << "成绩:" << score << endl;
    cout << "性别:" << gender << endl;

    // 2. 字符串转数字
    string num_str = "12345";
    istringstream num_iss(num_str);
    int num;
    num_iss >> num;
    cout << "\n字符串\"" << num_str << "\"转整数:" << num << endl;

    // 3. 处理CSV格式字符串
    string csv = "2023,12,25";
    istringstream csv_iss(csv);
    int year, month, day;
    char delim;  // 用于接收分隔符','
    csv_iss >> year >> delim >> month >> delim >> day;
    cout << "\nCSV解析结果:" << year << "年" << month << "月" << day << "日" << endl;

    return 0;
}

11.4 输入输出流

   fstream是兼具输入和输出功能的流类,可同时进行文件的读写操作,需指定ios::in | ios::out模式。

文件打开模式组合

模式组合

功能

`ios::in

ios::out`

读写模式,文件必须存在

`ios::in

ios::out

ios::trunc`

读写模式,文件不存在则创建,存在则清空

`ios::in

ios::out

ios::app`

读写模式,写操作追加到文件末尾

`ios::in

ios::out

ios::binary`

二进制读写模式

代码示例:输入输出流使用
代码语言:javascript
复制
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main() {
    // 创建并写入初始内容
    ofstream ofs("io_stream.txt");
    if (!ofs) {
        cerr << "创建文件失败!" << endl;
        return 1;
    }
    ofs << "初始内容:Hello World!\n这是第二行。" << endl;
    ofs.close();

    // 以读写模式打开文件
    fstream fs("io_stream.txt", ios::in | ios::out);
    if (!fs) {
        cerr << "文件打开失败!" << endl;
        return 1;
    }

    // 读取第一行内容
    string first_line;
    getline(fs, first_line);
    cout << "读取第一行:" << first_line << endl;

    // 移动到文件开头并写入新内容(覆盖原有内容)
    fs.seekp(0, ios::beg);  // 设置写位置到开头
    fs << "修改后的第一行";  // 写入内容(长度不足会保留原内容剩余部分)
    
    // 移动到文件末尾追加内容
    fs.seekp(0, ios::end);  // 设置写位置到末尾
    fs << "\n这是追加的内容" << endl;

    // 验证修改结果
    fs.seekg(0, ios::beg);  // 移动读位置到开头
    cout << "\n文件完整内容:" << endl;
    string line;
    while (getline(fs, line)) {  // 循环读取所有行
        cout << line << endl;
    }

    fs.close();
    return 0;
}

11.5 综合实例 —— 对个人银行账户管理程序的改进

本实例基于文件流实现银行账户数据的持久化存储,将账户信息保存到文件中,下次运行时可读取恢复。

需求分析
  • 实现银行账户类(包含账号、姓名、余额属性)
  • 支持存款、取款操作
  • 支持账户信息保存到文件
  • 支持从文件加载账户信息
完整代码实现
代码语言:javascript
复制
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <limits>  // 新增:包含numeric_limits所需的头文件

using namespace std;

// 银行账户类
class BankAccount {
private:
    string accountId;  // 账号
    string name;       // 姓名
    double balance;    // 余额

public:
    // 构造函数
    BankAccount(string id = "", string n = "", double b = 0.0)
        : accountId(id), name(n), balance(b) {}

    // 存款
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            cout << "存款成功!存入金额:" << amount << endl;
        } else {
            cout << "存款金额必须为正数!" << endl;
        }
    }

    // 取款
    bool withdraw(double amount) {
        if (amount <= 0) {
            cout << "取款金额必须为正数!" << endl;
            return false;
        }
        if (amount > balance) {
            cout << "余额不足!当前余额:" << balance << endl;
            return false;
        }
        balance -= amount;
        cout << "取款成功!取出金额:" << amount << endl;
        return true;
    }

    // 显示账户信息
    void showInfo() const {
        cout << "\n===== 账户信息 =====" << endl;
        cout << "账号:" << accountId << endl;
        cout << "姓名:" << name << endl;
        cout << "余额:" << balance << "元" << endl;
        cout << "====================" << endl;
    }

    // 保存账户信息到文件(友元函数)
    friend ofstream& operator<<(ofstream& ofs, const BankAccount& acc) {
        ofs << acc.accountId << endl;
        ofs << acc.name << endl;
        ofs << acc.balance << endl;
        return ofs;
    }

    // 从文件读取账户信息
    friend ifstream& operator>>(ifstream& ifs, BankAccount& acc) {
        ifs >> acc.accountId;
        ifs >> acc.name;
        ifs >> acc.balance;
        // 忽略行尾的换行符(需要#include <limits>)
        ifs.ignore(numeric_limits<streamsize>::max(), '\n');
        return ifs;
    }

    // 获取账号(用于查找)
    string getAccountId() const { return accountId; }
};

// 账户管理类
class AccountManager {
private:
    vector<BankAccount> accounts;  // 账户集合
    string filename;               // 保存文件名称

public:
    // 构造函数:指定保存文件
    AccountManager(string fn) : filename(fn) {
        loadAccounts();  // 初始化时加载账户
    }

    // 加载账户信息
    void loadAccounts() {
        ifstream ifs(filename);
        if (!ifs) {
            cout << "首次运行,未找到账户文件,将创建新文件。" << endl;
            return;
        }

        BankAccount acc;
        while (ifs >> acc) {  // 循环读取所有账户
            accounts.push_back(acc);
        }
        cout << "成功加载 " << accounts.size() << " 个账户信息。" << endl;
    }

    // 保存账户信息
    void saveAccounts() {
        ofstream ofs(filename);
        if (!ofs) {
            cerr << "保存账户失败!" << endl;
            return;
        }

        for (const auto& acc : accounts) {
            ofs << acc;  // 使用重载的<<运算符
        }
        cout << "成功保存 " << accounts.size() << " 个账户信息。" << endl;
    }

    // 添加账户
    void addAccount(const BankAccount& acc) {
        accounts.push_back(acc);
        saveAccounts();  // 立即保存
    }

    // 查找账户
    BankAccount* findAccount(const string& id) {
        for (auto& acc : accounts) {
            if (acc.getAccountId() == id) {
                return &acc;
            }
        }
        return nullptr;
    }

    // 显示所有账户
    void showAllAccounts() {
        if (accounts.empty()) {
            cout << "暂无账户信息!" << endl;
            return;
        }
        cout << "\n===== 所有账户信息 =====" << endl;
        for (const auto& acc : accounts) {
            acc.showInfo();
        }
    }
};

// 主函数:菜单交互
int main() {
    AccountManager manager("bank_accounts.txt");
    int choice;

    do {
        cout << "\n===== 银行账户管理系统 =====" << endl;
        cout << "1. 添加账户" << endl;
        cout << "2. 查找账户" << endl;
        cout << "3. 存款" << endl;
        cout << "4. 取款" << endl;
        cout << "5. 显示所有账户" << endl;
        cout << "0. 退出" << endl;
        cout << "请选择操作:";
        cin >> choice;

        switch (choice) {
            case 1: {
                string id, name;
                double balance;
                cout << "请输入账号:";
                cin >> id;
                cout << "请输入姓名:";
                cin >> name;
                cout << "请输入初始余额:";
                cin >> balance;
                manager.addAccount(BankAccount(id, name, balance));
                break;
            }
            case 2: {
                string id;
                cout << "请输入要查找的账号:";
                cin >> id;
                BankAccount* acc = manager.findAccount(id);
                if (acc) {
                    acc->showInfo();
                } else {
                    cout << "未找到账号为 " << id << " 的账户!" << endl;
                }
                break;
            }
            case 3: {
                string id;
                double amount;
                cout << "请输入账号:";
                cin >> id;
                BankAccount* acc = manager.findAccount(id);
                if (acc) {
                    cout << "请输入存款金额:";
                    cin >> amount;
                    acc->deposit(amount);
                    manager.saveAccounts();  // 保存修改
                } else {
                    cout << "未找到账号为 " << id << " 的账户!" << endl;
                }
                break;
            }
            case 4: {
                string id;
                double amount;
                cout << "请输入账号:";
                cin >> id;
                BankAccount* acc = manager.findAccount(id);
                if (acc) {
                    cout << "请输入取款金额:";
                    cin >> amount;
                    if (acc->withdraw(amount)) {
                        manager.saveAccounts();  // 保存修改
                    }
                } else {
                    cout << "未找到账号为 " << id << " 的账户!" << endl;
                }
                break;
            }
            case 5:
                manager.showAllAccounts();
                break;
            case 0:
                cout << "谢谢使用,再见!" << endl;
                break;
            default:
                cout << "无效选择,请重新输入!" << endl;
        }
    } while (choice != 0);

    return 0;
}

11.6 深度探索

11.6.1 宽字符、宽字符串与宽流

C++ 支持宽字符(wchar_t)用于处理 Unicode 字符,对应的宽流类用于宽字符输入输出,核心宽流类如下:

窄流类

宽流类

功能

cin

wcin

宽字符控制台输入

cout

wcout

宽字符控制台输出

ifstream

wifstream

宽字符文件输入

ofstream

wofstream

宽字符文件输出

stringstream

wstringstream

宽字符串流

代码示例:宽字符与宽流使用
代码语言:javascript
复制
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <locale>  // 用于本地化设置

using namespace std;

int main() {
    // 设置本地化(支持中文等宽字符)
    setlocale(LC_ALL, "");  // Windows使用"chs",Linux/macOS使用"zh_CN.UTF-8"

    // 1. 宽字符控制台输出
    wcout << L"===== 宽字符控制台输出 =====" << endl;
    wchar_t wch = L'中';  // 宽字符用L前缀
    wstring wstr = L"宽字符串:Hello 世界!";  // 宽字符串用L前缀
    wcout << L"单个宽字符:" << wch << endl;
    wcout << L"宽字符串:" << wstr << endl << endl;

    // 2. 宽字符文件操作
    wofstream wofs("wide_char.txt");
    if (wofs) {
        wofs << L"写入宽字符文件:你好,C++宽流!" << endl;
        wofs << L"数字:123,字符:π" << endl;  // 可写入特殊字符
        wofs.close();
        wcout << L"宽字符文件写入完成!" << endl;
    } else {
        wcerr << L"宽字符文件打开失败!" << endl;
    }

    // 读取宽字符文件
    wifstream wifs("wide_char.txt");
    if (wifs) {
        wcout << endl << L"读取宽字符文件内容:" << endl;
        wstring wline;
        while (getline(wifs, wline)) {  // 宽字符getline
            wcout << wline << endl;
        }
        wifs.close();
    }

    // 3. 宽字符串流
    wstringstream wss;
    wss << L"宽字符串流拼接:" << wstr << L" 长度:" << wstr.size();
    wcout << endl << L"宽字符串流结果:" << wss.str() << endl;

    return 0;
}
11.6.2 对象的串行化

     对象串行化(Serialization)是将对象状态转换为可存储或传输的格式(如二进制)的过程,便于对象持久化或网络传输。C++ 中可通过write()read()实现简单的对象串行化。

代码示例:对象串行化实现
代码语言:javascript
复制
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

// 可串行化的学生类
class Student {
private:
    int id;
    string name;  // 注意:string内部有指针,需特殊处理
    float score;

public:
    Student(int i = 0, string n = "", float s = 0.0f)
        : id(i), name(n), score(s) {}

    // 显示信息
    void show() const {
        cout << "学号:" << id << ",姓名:" << name << ",成绩:" << score << endl;
    }

    // 串行化(保存对象到文件)
    void serialize(ofstream& ofs) const {
        // 1. 写入基本类型
        ofs.write(reinterpret_cast<const char*>(&id), sizeof(id));
        
        // 2. 写入string:先写长度,再写内容
        size_t name_len = name.size();
        ofs.write(reinterpret_cast<const char*>(&name_len), sizeof(name_len));
        ofs.write(name.c_str(), name_len);  // 写入字符串内容
        
        // 3. 写入基本类型
        ofs.write(reinterpret_cast<const char*>(&score), sizeof(score));
    }

    // 反串行化(从文件恢复对象)
    void deserialize(ifstream& ifs) {
        // 1. 读取基本类型
        ifs.read(reinterpret_cast<char*>(&id), sizeof(id));
        
        // 2. 读取string:先读长度,再读内容
        size_t name_len;
        ifs.read(reinterpret_cast<char*>(&name_len), sizeof(name_len));
        char* name_buf = new char[name_len + 1];
        ifs.read(name_buf, name_len);
        name_buf[name_len] = '\0';
        name = name_buf;
        delete[] name_buf;  // 释放临时缓冲区
        
        // 3. 读取基本类型
        ifs.read(reinterpret_cast<char*>(&score), sizeof(score));
    }
};

int main() {
    // 创建对象
    Student s1(1001, "张三", 95.5f);
    Student s2(1002, "李四", 88.0f);
    cout << "原始对象:" << endl;
    s1.show();
    s2.show();

    // 串行化对象到文件
    ofstream ofs("students_serialize.dat", ios::binary);
    if (ofs) {
        s1.serialize(ofs);
        s2.serialize(ofs);
        ofs.close();
        cout << "\n对象串行化完成!" << endl;
    } else {
        cerr << "文件打开失败!" << endl;
        return 1;
    }

    // 反串行化恢复对象
    Student s3, s4;
    ifstream ifs("students_serialize.dat", ios::binary);
    if (ifs) {
        s3.deserialize(ifs);
        s4.deserialize(ifs);
        ifs.close();
        cout << "\n反串行化恢复对象:" << endl;
        s3.show();
        s4.show();
    } else {
        cerr << "文件打开失败!" << endl;
        return 1;
    }

    return 0;
}

11.7 小结

本章详细介绍了 C++ 流类库与输入输出的核心知识,主要包括:

  1. 流类库基础:流的概念、类层次结构(iosistreamostream等核心类)
  2. 输出流操作cout控制台输出、ofstream文件输出、ostringstream字符串输出,以及格式控制
  3. 输入流操作cin控制台输入、ifstream文件输入、istringstream字符串输入,以及灵活的读取函数
  4. 双向流fstream实现文件的同时读写
  5. 综合应用:通过银行账户管理程序实践文件流的持久化存储
  6. 高级主题:宽字符处理与对象串行化技术

        掌握流类库的使用,能让你灵活处理各种输入输出场景,从简单的控制台交互到复杂的文件操作和对象持久化,为实际项目开发打下坚实基础。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • 11.1 I/O 流的概念及流类库结构
    • 1.1.1 流的基本概念
    • 1.1.2 流类库结构
      • 流类库核心类图(PlantUML)
      • 流类库知识思维导图
  • 11.2 输出流
    • 11.2.1 构造输出流对象
      • 代码示例:构造输出流对象
    • 11.2.2 使用插入运算符和操纵符
      • 常用输出操纵符
      • 代码示例:插入运算符与操纵符使用
    • 11.2.3 文件输出流成员函数
      • 常用输出流成员函数
      • 代码示例:文件输出流成员函数使用
    • 11.2.4 二进制输出文件
      • 代码示例:二进制输出文件操作
    • 11.2.5 字符串输出流
      • 代码示例:字符串输出流使用
  • 11.3 输入流
    • 11.3.1 构造输入流对象
      • 代码示例:构造输入流对象
    • 11.3.2 使用提取运算符
      • 代码示例:提取运算符使用
    • 11.3.3 输入流操纵符
      • 代码示例:输入流操纵符使用
    • 11.3.4 输入流相关函数
      • 常用输入流成员函数
      • 代码示例:输入流成员函数使用
    • 11.3.5 字符串输入流
      • 代码示例:字符串输入流使用
  • 11.4 输入输出流
    • 文件打开模式组合
    • 代码示例:输入输出流使用
  • 11.5 综合实例 —— 对个人银行账户管理程序的改进
    • 需求分析
    • 完整代码实现
  • 11.6 深度探索
    • 11.6.1 宽字符、宽字符串与宽流
      • 代码示例:宽字符与宽流使用
    • 11.6.2 对象的串行化
      • 代码示例:对象串行化实现
  • 11.7 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档