C++ 学习 C++ I/O流 2025年6月17日17:02:34

发布于:2025-06-18 ⋅ 阅读:(13) ⋅ 点赞:(0)

C++ I/O流

C++ I/O流 是 标准库提供的面向对象的输入输出系统,比 C 语言的 printf/scanf 更安全、更灵活

 必要的头文件:

#include <iostream>  // 标准输入输出
#include <fstream>   // 文件操作
#include <sstream>   // 字符串流

对象 描述 对应设备
cin 标准输入(键盘) stdin
cout 标准输出(屏幕) stdout
cerr 标准错误(无缓冲) stderr
clog 标准日志(有缓冲) stderr
注意:
std::cerr 是无缓冲的:它会立即将内容发送到终端,确保错误信息不被延迟。

std::cout 是行缓冲的:如果没有换行符或手动刷新,可能不会立即显示。

两个都是输出到终端  cerr是错误 输出 即使程序崩溃也会输出 

调用代码示例: 
int num;
std::cout << "Enter a number: ";  // 输出 行缓冲 正常输出
std::cin >> num;                  // 输入 num=键盘输入值
std::cerr << "Error occurred!";   // 错误输出 无缓冲 立即显示 (即使程序崩溃)
std::clog << "Warning message";   // 日志输出 有缓冲 可能不会立即显示(延迟输出)

格式化输出:

控制符   

std::endl                     换行并刷新缓冲区
std::setw(n)                设置字段宽度为 n (仅对下一个输出有效)
std::setprecision(n)    设置浮点数精度为 n 默认精度是6位 当n设置为4时 精度4位
std::fixed                    固定小数格式
std::scientific             科学计数法格式

std::hex                      十六进制

std::oct                          八进制

std::dec                         十进制

 示例代码展示:
#include <iostream>
#include <iomanip>  // 包含控制符所需的头文件

int main() {
    int num = 42;
    double pi = 3.141592653589793;

    // 1. std::endl:换行并刷新缓冲区
    std::cout << "Line 1" << std::endl;
    std::cout << "Line 2" << std::endl;

    // 2. std::setw(n):设置字段宽度(仅对下一个输出有效)
    std::cout << "\n格式化表格:" << std::endl;
    std::cout << std::setw(10) << "Name" << std::setw(10) << "Age" << std::endl;
    std::cout << std::setw(10) << "Alice" << std::setw(10) << 25 << std::endl;
    std::cout << std::setw(10) << "Bob" << std::setw(10) << 30 << std::endl;

    // 3. std::setprecision(n) + std::fixed:控制浮点数精度和小数格式
    std::cout << "\n默认精度(6位):" << pi << std::endl;
    std::cout << "固定小数(4位):" << std::fixed << std::setprecision(4) << pi << std::endl;

    // 4. std::scientific:科学计数法格式
    std::cout << "科学计数法(2位):" << std::scientific << std::setprecision(2) << pi << std::endl;

    // 恢复默认格式(避免影响后续输出)
    std::cout.unsetf(std::ios::fixed | std::ios::scientific);
    std::cout << "\n恢复默认格式:" << pi << std::endl;

    return 0;
}

//输出效果展示 

Line 1
Line 2

格式化表格:
      Name       Age
     Alice        25
       Bob        30

默认精度(6位):3.14159
固定小数(4位):3.1416
科学计数法(2位):3.14e+00

恢复默认格式:3.14159

操作文件(文件流File I/O)

关键头文件:
#include <fstream> //操作文件流
#include <string>  //字符串操作 例如std::getline
#include <iostream>//标准输出 例如 std::cout
示例代码:
std::ofstream out("test1.txt"); //打开该文件如果没有 默认创建文件 out自定义名

if (out.is_open()) //检测是否打开文件 
    {   out<<"hello world\n 2025年6月17日17:33:05 \n"; //写入值 到文件中
        std::cout << "open file success"; //输出提示 文件已打开

    }
    else
    {
        std::cerr << "No find file";
    }
    out.close();


//以下是读取文件
   std::ifstream in("test1.txt"); //找不到文件会报错 in是自定义名
    if (in.is_open())
    {  
        std::string strs;
        while(std::getline(in,strs))
        {
            std::cout<<strs<<std::endl;
        }

    }
    else
    {
        std::cerr << "No find file";
    }
    in.close();
 文件处理模式选择:
std::ofstream log("log.txt", std::ios::app);  // 追加模式
模式标志 描述 默认情况
std::ios::in 读取模式(文件必须存在) ifstream 默认包含
std::ios::out 写入模式(创建或清空文件) ofstream 默认包含
std::ios::app 追加模式(写入时保留原内容,总是在末尾添加) 非默认
std::ios::ate 打开时定位到文件末尾(可读写) 非默认
std::ios::trunc 清空文件(需配合 out 使用) out 隐含此模式
std::ios::binary 二进制模式(避免换行符转换) 非默认
关于二进制模式的使用: 
#include <cstddef> // 用于 std::byte  c++17 以上版本

std::ofstream binfile("data.bin", std::ios::binary | std::ios::out);
binfile.write(reinterpret_cast<const std::byte*>(&some_data), sizeof(some_data));

或者:
std::ofstream binfile("data.bin", std::ios::binary | std::ios::out);
binfile.write(reinterpret_cast<char*>(&some_data), sizeof(some_data));

或者:
char buffer[1024];
// ... 填充 buffer ...
binfile.write(buffer, sizeof(buffer));

注意事项:
在 C++17 之前        char* 是 C/C++ 中表示“字节”的传统类型。
现代方式(C++17 起):std::byte*  需要包含 <cstddef> 头文件!!!
&some_data
取 some_data 的内存地址,获得指向数据的指针

sizeof(some_data)
计算 some_data 的字节大小。

确保写入的字节数与实际数据大小一致。

std::ofstream::write() 的函数原型要求传入一个 char* 或 const std::byte* 类型的指针,指向要写入的数据的起始地址。但实际数据可能是任意类型(如 int、double、结构体等),因此需要通过 reinterpret_cast 进行指针类型转换。

reinterpret_cast 的语义
将指针 重新解释 为另一种类型,不改变底层二进制数据。
它是低级别的、不安全的转换,但在此场景下是必要的。

组合使用: 

in | out:读写已存在文件(不自动创建)

out | trunc:等同于默认 ofstream 行为

out | app:等同于 app 单独使用

举例说明:

将不会再默认创建文件 当文件没有找到时

  std::ofstream insaa("test2.txt",std::ios::in); 
//将不会再默认创建文件 当文件没有找到时
注意事项:

使用 app 模式时,所有写入都在末尾,无法修改文件中间内容

ate 只影响初始位置,不影响写入行为

文件流对象在析构时会自动关闭文件,但显式调用 close() 是好习惯

字符串流(String Stream)

必要头文件:
#include <sstream>
将数据转换为字符串:
std::ostringstream oss;
oss << "Age: " << 25 << ", PI: " << 3.14; //字符串拼接
std::string str = oss.str();  // 获取字符串
std::cout << str;  // 输出: Age: 25, PI: 3.14

//std::ostringstream 是 C++ 标准库中的一个类,用于 将数据以字符串形式写入内存(而不是文件或屏幕)
从字符串解析数据:
std::istringstream iss("10 3.14 hello");
int num; double val; std::string word;
iss >> num >> val >> word;  // 解析为变量

//std::istringstream 是 C++ 标准库中用于 从字符串中读取数据 的输入流类(定义在 <sstream> 头文件中)

流状态函数判断

函数 作用 典型触发场景
stream.good() 检查流是否完全正常(无任何错误,可继续操作)。 - 所有操作成功时返回 true
- 其他状态函数均为 false 时返回 true
stream.eof() 检查是否到达文件末尾(End Of File)。 - 读取文件时遇到文件结束符。
注意:仅表示“读完”,不一定是错误!
stream.fail() 检查是否发生非致命错误(可恢复,如类型不匹配、格式错误)。 - 尝试将 "abc" 读入 int 变量。
- 输入格式不符合预期。
stream.bad() 检查是否发生致命错误(不可恢复,如文件损坏、流被意外关闭)。 - 文件读取时磁盘被拔出。
- 流缓冲区内存不足。

状态之间的关系:

good() == true
所有其他状态函数(eof()fail()bad())必须均为 false,表示流完全正常。

状态优先级
bad() > fail() > eof()(即 bad() 的优先级最高)。

good() eof() fail() bad() 含义
true false false false 流完全正常。
false true false false 正常到达文件末尾。
false false true false 发生非致命错误(如类型错误)。
false false true true 发生致命错误(如文件损坏)。
范例程序(直观展示用法):
#include <iostream>
#include <fstream>
#include <sstream>

void check_stream_state(std::istream& stream, const std::string& stream_name) {
    std::cout << "\n[" << stream_name << "状态检查]\n";
    
    if (stream.good()) {
        std::cout << "✅ 流状态正常,可以继续操作\n";
    } else {
        std::cout << "⚠️ 流状态异常\n";
    }

    if (stream.eof()) {
        std::cout << "📌 已到达文件/字符串末尾\n";
    }

    if (stream.fail()) {
        std::cout << "🔄 发生非致命错误(如类型不匹配、格式错误)\n";
        stream.clear(); // 清除错误状态以便继续操作
    }

    if (stream.bad()) {
        std::cout << "❌ 发生致命错误(如文件损坏、硬件故障)\n";
    }
}

int main() {
    // 示例1:文件流正常读取
    std::cout << "==== 示例1:正常读取文件 ====\n";
    std::ifstream file("data.txt");
    if (file) {
        int value;
        file >> value;
        check_stream_state(file, "文件流");
        file.close();
    } else {
        std::cout << "无法打开文件\n";
    }

    // 示例2:类型不匹配错误
    std::cout << "\n==== 示例2:类型不匹配 ====\n";
    std::istringstream number_stream("abc"); // 不是数字
    int number;
    number_stream >> number;
    check_stream_state(number_stream, "字符串流");

    // 示例3:到达字符串末尾
    std::cout << "\n==== 示例3:正常到达末尾 ====\n";
    std::istringstream eof_stream("123");
    int val;
    eof_stream >> val;
    check_stream_state(eof_stream, "EOF测试流");

    return 0;
}

//输出效果
==== 示例1:正常读取文件 ====

[文件流状态检查]
✅ 流状态正常,可以继续操作

==== 示例2:类型不匹配 ====

[字符串流状态检查]
⚠️ 流状态异常
🔄 发生非致命错误(如类型不匹配、格式错误)

==== 示例3:正常到达末尾 ====

[EOF测试流状态检查]
⚠️ 流状态异常
📌 已到达文件/字符串末尾
 简化范例程序(更直观):
std::ifstream file("data.txt");
if (!file) {  // 等价于 file.fail() 文件是否发生错误 
    std::cerr << "Failed to open file!";
}

自定义类型I/O 

class Point {
public:
    int x, y;
    //  friend  友元声明(可访问 private 成员)

    friend std::ostream& operator<<(std::ostream& os, const Point& p) {
        return os << "(" << p.x << ", " << p.y << ")";
    }
    friend std::istream& operator>>(std::istream& is, Point& p) {
        return is >> p.x >> p.y;
    }
};

// 使用
Point p{1, 2};
std::cout << p;  // 输出: (1, 2)
std::cin >> p;   // 输入: 3 4 → p={3,4}

=============================================
关键点:
friend 声明:
使这个函数能访问 Point 的 private 成员(虽然这里成员是 public,但习惯上仍用 friend)

返回 ostream&:
支持链式调用(如 cout << p1 << p2)

operator<< 是 输出流运算符,通常用于自定义类型的输出格式化。
operator>> 是 输入流运算符,用于从输入流(如 std::cin 或文件流)中提取数据到自定义类型的对象中

参数:

os:输出流对象(如 std::cout)

p:要输出的 Point 对象(const 引用避免拷贝)

实现:
将点格式化为 (x, y) 的形式(如 (1, 2))

缓冲区管理

操作 作用
stream.flush() 手动刷新缓冲区
std::endl 换行 + 刷新缓冲区
std::unitbuf 每次操作后自动刷新
调用示例:
std::cout << "Hello" << std::flush;  // 立即输出
std::cerr << std::unitbuf;           // 关闭缓冲(实时输出错误)

总结

功能 工具 示例
标准 I/O cin/cout/cerr cout << "Hello";
文件操作 ifstream/ofstream ofstream file("data.txt");
字符串格式化 istringstream/ostringstream oss << "Value: " << 42;
自定义类型 重载 << 和 >> cout << myObject;
格式化控制 <iomanip> 头文件 setprecision(2)


网站公告

今日签到

点亮在社区的每一天
去签到