C++:IO库
一、C++ IO库的架构
C++标准库中的IO系统基于流(Stream)的概念,分为三层结构:
- 流对象(如
cin,cout,fstream) - 流缓冲区(streambuf,负责底层数据处理)
- 数据源/目的地(如内存、文件、控制台)
主要头文件:
<ios>:基础IO类定义<istream>/<ostream>:输入输出流<iostream>:控制台输入输出<fstream>:文件流<sstream>:字符串流

二、C++ IO类的层次结构
1. 基础类
- ios_base:这是所有 IO 类的基类,它定义了一些通用的状态标志、格式标志和事件处理机制。它不涉及具体的输入输出操作,主要用于管理和维护 IO 流的状态和格式。
- ios:继承自
ios_base,它包含了流的缓冲区指针和一些状态信息,为后续的输入输出操作提供了基础。
2. 输入输出类
- istream:用于输入操作的类,继承自
ios。它定义了一系列用于从流中读取数据的成员函数,如operator>>、get、getline等。 - ostream:用于输出操作的类,同样继承自
ios。它定义了一系列用于向流中写入数据的成员函数,如operator<<、put等。 - iostream:继承自
istream和ostream,它既支持输入操作,也支持输出操作。
3. 文件 IO 类
- ifstream:继承自
istream,用于从文件中读取数据。 - ofstream:继承自
ostream,用于向文件中写入数据。 - fstream:继承自
iostream,支持对文件的读写操作。
4. 字符串 IO 类
- istringstream:继承自
istream,用于从字符串中读取数据,就像从输入流中读取一样。 - ostringstream:继承自
ostream,用于将数据写入字符串中。 - stringstream:继承自
iostream,支持对字符串的读写操作。
层次结构示意图

三、核心基类详解
1. ios_base(位于<ios>)
- 功能:定义所有流类的公共属性和状态
- 关键成员:
// 格式化标志 fmtflags setf(fmtflags flags); // 设置格式 streamsize precision(streamsize prec); // 浮点数精度// 流状态 iostate rdstate() const; // 获取当前状态 bool good() const; // 流是否正常 bool eof() const; // 是否到达流末尾
2. ios(继承自ios_base,位于<ios>)
- 功能:管理流缓冲区和错误状态
- 关键成员:
class ios : public ios_base {
public:// 流状态管理bool good() const; // 状态正常bool eof() const; // 到达文件尾bool fail() const; // 可恢复错误bool bad() const; // 严重错误void clear(iostate state = goodbit);iostate rdstate() const;// 流缓冲区操作streambuf* rdbuf() const;streambuf* rdbuf(streambuf* sb);
};
职责:
- 维护流状态(goodbit、eofbit、failbit、badbit)
- 管理关联的
streambuf对象
3、输入输出流基类
1. istream(输入流,位于<istream>)
- 功能:处理输入操作
- 关键成员:
// 基础输入 istream& operator>>(T& val); // 重载的提取操作符 int get(); // 读取单个字符 istream& getline(char* s, streamsize n); // 读取一行// 高级操作 streamsize readsome(char* s, streamsize n); // 读取可用数据//简易完整版 class istream : virtual public ios { public:// 基础输入istream& operator>>(int& val);istream& operator>>(string& str);// 非格式化输入int get();istream& get(char& c);istream& getline(char* s, streamsize n);// 高级操作streamsize readsome(char* s, streamsize n);istream& ignore(streamsize n = 1, int delim = EOF); };
关键方法:
>>运算符重载(格式输入)get()读取单个字符
2. ostream(输出流,位于<ostream>)
- 功能:处理输出操作
- 关键成员:
// 基础输出 ostream& operator<<(T val); // 重载的插入操作符 ostream& put(char c); // 输出单个字符// 刷新缓冲区 ostream& flush(); // 强制刷新缓冲区class ostream : virtual public ios { public:// 基础输出ostream& operator<<(int val);ostream& operator<<(const char* str);// 非格式化输出ostream& put(char c);ostream& write(const char* s, streamsize n);// 控制输出位置pos_type tellp();ostream& seekp(pos_type pos); };
关键方法:
<<运算符重载(格式输出)put()输出单个字符write()原始字节输出
3. iostream(继承自istream和ostream,位于<istream>)
- 功能:同时支持输入输出的流(如标准控制台流)
class iostream : public istream, public ostream {
public:// 多继承类,支持双向操作
};
典型应用:
- 标准控制台流
cin/cout/cerr的实现
四、<< 、>>运算符
1. << 运算符(输出运算符)(像水波扩撒)
基本用途
<< 运算符一般用于向输出流里写入数据,最常见的输出流便是 std::cout,它代表标准输出(通常是控制台)。此运算符把数据发送到输出流,然后输出流会把数据显示在对应的设备上。
语法
output_stream << data;//data->output_stream
这里的 output_stream 是输出流对象,像 std::cout、std::ofstream(用于文件输出)等;data 是要输出的数据,可以是基本数据类型(例如 int、double、char 等),也可以是自定义类型。
示例代码
#include <iostream>int main() {int num = 10;double pi = 3.14;std::string str = "Hello, World!";// 输出整数std::cout << "整数: " << num << std::endl;// 输出浮点数std::cout << "浮点数: " << pi << std::endl;// 输出字符串std::cout << "字符串: " << str << std::endl;return 0;
}
std::cout << "整数: ":把字符串"整数: "发送到标准输出流。<< num:接着把变量num的值发送到标准输出流。<< std::endl:向标准输出流发送换行符,同时刷新输出缓冲区。
链式调用
<< 运算符支持链式调用,也就是可以连续使用该运算符来输出多个数据。
std::cout << "整数: " << num << ", 浮点数: " << pi << std::endl;
2. >> 运算符(输入运算符)(像水波扩散)
基本用途
>> 运算符通常用于从输入流读取数据,最常见的输入流是 std::cin,它代表标准输入(通常是键盘)。该运算符从输入流中提取数据,并将其存储到指定的变量中。
语法
input_stream >> variable;//input_stream->variable
这里的 input_stream 是输入流对象,如 std::cin、std::ifstream(用于文件输入)等;variable 是要存储数据的变量。
示例代码
#include <iostream>int main() {int num;double pi;std::string str;// 输入整数std::cout << "请输入一个整数: ";std::cin >> num;// 输入浮点数std::cout << "请输入一个浮点数: ";std::cin >> pi;// 输入字符串std::cout << "请输入一个字符串: ";std::cin >> str;// 输出输入的数据std::cout << "你输入的整数是: " << num << std::endl;std::cout << "你输入的浮点数是: " << pi << std::endl;std::cout << "你输入的字符串是: " << str << std::endl;return 0;
}
std::cin >> num:从标准输入流中读取一个整数,并将其存储到变量num中。std::cin >> pi:从标准输入流中读取一个浮点数,并将其存储到变量pi中。std::cin >> str:从标准输入流中读取一个字符串,并将其存储到变量str中。- 注意,
>>运算符在读取字符串时会忽略空白字符(如空格、制表符、换行符等),直到遇到下一个空白字符为止。
链式调用
>> 运算符也支持链式调用,可以连续使用该运算符来读取多个数据。
std::cin >> num >> pi;
3. 自定义类型的 >> 和 << 运算符重载
对于自定义类型,也可以重载 >> 和 << 运算符,以便支持自定义类型的输入输出操作。
示例代码
#include <iostream>
#include <string>class Person {
public:std::string name;int age;// 重载 << 运算符friend std::ostream& operator<<(std::ostream& os, const Person& p) {os << "姓名: " << p.name << ", 年龄: " << p.age;return os;}// 重载 >> 运算符friend std::istream& operator>>(std::istream& is, Person& p) {std::cout << "请输入姓名: ";is >> p.name;std::cout << "请输入年龄: ";is >> p.age;return is;}
};int main() {Person p;std::cin >> p;std::cout << p << std::endl;return 0;
}
operator<<:这是一个友元函数,用于将Person对象的信息输出到输出流中。operator>>:这也是一个友元函数,用于从输入流中读取数据,并将其存储到Person对象中。
五、文件流类(<fstream>)
1. ifstream(输入文件流)
class ifstream : public istream {
public:// 文件操作void open(const char* filename, ios_base::openmode mode = ios_base::in);void close();bool is_open() const;
};
- 典型用法:
ifstream fin("data.txt"); if (fin) { // 检查是否成功打开int value;fin >> value; }
2. ofstream(输出文件流)
class ofstream : public ostream {
public:// 支持与ifstream类似的文件操作
};
3. fstream(双向文件流)
- 继承自
iostream,支持读写操作:fstream file("data.txt", ios::in | ios::out);
4.示例:文件复制
ifstream in("source.txt", ios::binary);
ofstream out("dest.txt", ios::binary);if (in && out) {out << in.rdbuf(); // 利用流缓冲区直接复制
}
六、字符串流类(<sstream>)
1. istringstream(输入字符串流)
class istringstream : public istream {
public:string str() const; // 获取底层字符串void str(const string& s); // 设置底层字符串
};
2. ostringstream(输出字符串流)
class ostringstream : public ostream {
public:string str() const;
};
3. 典型应用:类型转换
#include <iostream>
#include <sstream>
using namespace std;
int main() {// 使用istringstream和ostringstream进行字符串与数值的转换string input = "42 3.14";istringstream iss(input);int a; double b;iss >> a >> b; // a=42, b=3.14ostringstream oss;oss << "Result: " << a*b;string output = oss.str(); // "Result: 133.56"
}
七、流缓冲区类(<streambuf>)
1.基本概念
流缓冲区类为 I/O 流提供了一个数据缓冲区,起到了数据的临时存储和传输的作用。当程序进行输入操作时,数据会先从输入设备(如文件、键盘)读取到流缓冲区中,然后程序再从缓冲区中获取数据;当进行输出操作时,数据会先被写入到流缓冲区,之后再由缓冲区将数据发送到输出设备(如文件、屏幕)。
2.层次结构
<streambuf> 头文件中定义的流缓冲区类形成了一个层次结构,主要的基类是 std::streambuf,它是所有流缓冲区类的基类,提供了通用的缓冲区管理接口。基于 std::streambuf 派生出了不同类型的流缓冲区类,以适应不同的 I/O 设备和操作:
std::filebuf:用于文件 I/O 的流缓冲区类,管理文件的读写操作。std::stringbuf:用于字符串 I/O 的流缓冲区类,管理字符串的读写操作。std::wstreambuf:宽字符版本的std::streambuf,用于处理宽字符的 I/O 操作。std::wfilebuf:宽字符版本的std::filebuf,用于宽字符文件的 I/O 操作。std::wstringbuf:宽字符版本的std::stringbuf,用于宽字符字符串的 I/O 操作。
3.主要成员函数
std::streambuf 类提供了许多成员函数,用于管理缓冲区和进行数据的读写操作:
- 缓冲区指针操作:
eback():返回输入缓冲区的起始指针。gptr():返回输入缓冲区的当前读取位置指针。egptr():返回输入缓冲区的结束指针。pbase():返回输出缓冲区的起始指针。pptr():返回输出缓冲区的当前写入位置指针。epptr():返回输出缓冲区的结束指针。
- 缓冲区填充和刷新操作:
underflow():当输入缓冲区为空时,该函数负责从输入设备读取数据填充缓冲区。overflow(int c = traits_type::eof()):当输出缓冲区已满时,该函数负责将缓冲区中的数据写入到输出设备,并刷新缓冲区。sync():刷新缓冲区,将缓冲区中的数据写入到输出设备。
- 数据读写操作:
sgetc():返回当前输入缓冲区的字符,但不移动读取指针。sbumpc():返回当前输入缓冲区的字符,并将读取指针向后移动一位。sputc(int c):将字符c写入到输出缓冲区,并将写入指针向后移动一位。sputn(const char_type* s, std::streamsize n):将字符串s中的n个字符写入到输出缓冲区。
4.自定义缓冲区(高级用法)
class MyBuffer : public streambuf {
protected:int_type overflow(int_type c) override {// 自定义输出处理return c;}
};// 使用自定义缓冲区
MyBuffer buf;
ostream custom_stream(&buf);
custom_stream << "Hello custom buffer!";
5.使用示例
以下是一个使用 std::filebuf 进行文件读写操作的示例:
#include <iostream>
#include <fstream>
#include <streambuf>int main() {// 打开文件并关联到 filebuf 对象std::filebuf fb;if (fb.open("test.txt", std::ios::out | std::ios::binary)) {// 写入数据到缓冲区const char* data = "Hello, World!";std::streamsize size = std::char_traits<char>::length(data);fb.sputn(data, size);// 刷新缓冲区并关闭文件fb.pubsync();fb.close();}// 打开文件并关联到 filebuf 对象进行读取操作if (fb.open("test.txt", std::ios::in | std::ios::binary)) {// 获取缓冲区的大小std::streamsize size = fb.pubseekoff(0, std::ios::end, std::ios::in);fb.pubseekpos(0, std::ios::in);// 读取数据到缓冲区char* buffer = new char[size];fb.sgetn(buffer, size);// 输出读取的数据std::cout << "读取的数据: " << buffer << std::endl;// 释放缓冲区内存并关闭文件delete[] buffer;fb.close();}return 0;
}
八、IO对象
C++标准库中IO对象不允许拷贝或赋值
1、基础现象验证
首先通过代码实验观察现象:
#include <fstream>
using namespace std;int main() {ifstream in1("test.txt");ifstream in2 = in1; // 编译错误:拷贝构造被禁用ofstream out;out = ofstream("data.txt"); // 编译错误:赋值操作被禁用
}
所有IO类型(如ifstream/ostringstream)均无法进行拷贝构造或赋值操作。
2、底层机制分析
1. 类定义实现(以C++11标准为例)
标准库通过删除拷贝控制成员显式禁用拷贝:
namespace std {class basic_ios : public ios_base {// ...basic_ios(const basic_ios&) = delete; // 拷贝构造删除basic_ios& operator=(const basic_ios&) = delete; // 赋值操作删除};template<typename _CharT, typename _Traits>class basic_ostream : virtual public basic_ios<_CharT, _Traits> {// 同样禁用拷贝构造和赋值};
}
3、禁止拷贝的核心原因
1. 资源唯一性原则
IO对象(如文件流)管理不可复制资源:
- 文件句柄(File Descriptor)
- 流缓冲区(streambuf)指针
- 流状态信息(goodbit/eofbit等)
如果允许拷贝,会导致多个对象操作同一个底层资源:
// 假设允许拷贝的假想场景
ifstream in1("data.txt");
ifstream in2 = in1;in1 >> x; // 读取部分数据
in2 >> y; // 无法确定读取位置,导致竞争状态
2. 状态一致性维护
每个IO对象维护独立状态:
- 当前文件读写位置(
tellg()/tellp()) - 错误状态标记(
rdstate()) - 格式控制标志(如精度、进制)
拷贝会导致状态信息的分裂,难以保证一致性。
3. 避免资源双重释放
如果允许拷贝,析构时会多次关闭同一个文件句柄:
// 假想允许拷贝的析构场景
{ifstream in1("data.txt");ifstream in2 = in1;
} // in2析构时关闭文件句柄 → in1析构时再次关闭已关闭的句柄 → 未定义行为
4、替代方案与正确用法
1. 使用引用传递
void process_input(istream& is) { // 通过引用操作流int val;is >> val;
}ifstream in("data.txt");
process_input(in); // 合法:传递引用
process_input(cin); // 同样适用标准输入流
2. 使用指针传递
void save_data(ostream* os) {*os << "Data: " << 42;
}ofstream out("data.txt");
save_data(&out); // 显式传递指针
3. 移动语义(C++11+)
虽然标准流对象不可移动,但可自定义包装类:
class StreamWrapper {unique_ptr<ostream> os;
public:StreamWrapper(ostream&& os) : os(os ? new ostream(os.rdbuf()) : nullptr) {}// 实现移动构造/赋值
};StreamWrapper sw1(ofstream("data.txt"));
StreamWrapper sw2 = std::move(sw1); // 合法移动
4. 共享流缓冲区
多个对象共享同一个流缓冲区(非拷贝对象):
ifstream in1("data.txt");
ifstream in2(in1.rdbuf()); // 构造新流但共享缓冲区in1.seekg(100); // 影响in2的读取位置
char c;
in2 >> c; // 从位置100开始读取
5、设计模式对比
| 类型 | 拷贝语义 | 移动语义(C++11) | 资源管理方式 |
|---|---|---|---|
std::fstream | ❌ 禁止 | ❌ 禁止 | 独占资源 |
std::string | ✅ 深拷贝 | ✅ 移动 | 值语义(COW优化) |
std::shared_ptr | ✅ 引用计数 | ✅ 移动 | 共享所有权 |
IO对象采用独占所有权模型,与unique_ptr类似,禁止拷贝以强制明确资源所有权。
6、陷阱
1. 函数返回值误区
// 错误示例:返回临时流对象
ifstream open_file(const string& name) {ifstream f(name);return f; // 尝试拷贝 → 编译错误
}// 正确做法:返回包装类或使用引用参数
void open_file(const string& name, ifstream& out) {out.open(name);
}
2. 容器存储问题
vector<ofstream> streams; // 编译错误:元素需要可拷贝// 正确做法:存储指针或包装类
vector<unique_ptr<ofstream>> streams;
streams.emplace_back(make_unique<ofstream>("log1.txt"));
九、条件状态
1、流状态的核心作用
IO流对象(如ifstream/cin)通过状态标志位实时反映操作结果,开发者可通过这些状态:
- 检测输入/输出是否成功
- 区分错误类型(可恢复错误 vs 致命错误)
- 控制程序流(重试/终止)
- 实现异常驱动式错误处理
2、状态标志位详解
在ios_base类中定义的4个核心状态标志(通过位掩码实现):
| 标志位 | 值 | 触发场景 |
|---|---|---|
goodbit | 0x00 | 流处于正常状态(无错误) |
eofbit | 0x01 | 到达文件末尾(End-of-File)或输入流结束(如Ctrl+D) |
failbit | 0x02 | 逻辑错误(如类型不匹配),可恢复错误 |
badbit | 0x04 | 物理错误(如磁盘损坏、无效文件指针),流已不可用 |
组合状态示例:
goodbit | eofbit→ 无效(实际状态由位运算决定)failbit | badbit→ 严重错误
3、状态检测方法
1. 成员函数检测
ifstream in("data.txt");// 直接检测函数
if (in.good()) { /* 流正常 */ }
if (in.eof()) { /* 到达文件尾 */ }
if (in.fail()) { /* 可恢复错误 */ }
if (in.bad()) { /* 物理损坏 */ }// 综合检测(等价于 !fail() && !bad() )
if (in) { /* 流可用 */ } // operator bool()重载
2. 位掩码操作
ios_base::iostate state = in.rdstate();if (state & ios::eofbit) {cout << "到达文件末尾" << endl;
}
if (state & (ios::failbit | ios::badbit)) {cout << "发生错误" << endl;
}
4、状态管理操作
1. 状态清除(clear)
int value;
cin >> value;if (cin.fail()) { // 输入非数字时触发cin.clear(); // 清除错误状态(恢复到goodbit)cin.ignore(1000, '\n'); // 跳过无效输入// 重试逻辑...
}
注意:clear()默认参数为goodbit,可指定具体状态:
in.clear(ios::goodbit | ios::eofbit); // 强制设置特定状态
2. 设置异常掩码(exceptions)
ifstream in;
in.exceptions(ios::failbit | ios::badbit); // 设置触发异常的标志位try {in.open("nonexist.txt"); // 打开失败触发ios::failure异常int val;in >> val; // 读取失败同样触发异常
}
catch (const ios::failure& e) {cerr << "IO异常: " << e.what() << endl;
}
5、典型应用场景
1. 安全读取循环
vector<int> values;
int tmp;while (cin >> tmp) { // 自动检测operator>>的返回流状态values.push_back(tmp);
}if (cin.eof()) {cout << "正常结束(到达EOF)" << endl;
}
else if (cin.fail()) {cout << "输入格式错误" << endl;cin.clear();
}
2. 文件读取容错
ifstream in("data.bin", ios::binary);
char buffer[1024];while (in.read(buffer, sizeof(buffer))) {// 正常处理数据块
}if (in.eof()) {// 处理最后不完整的数据块streamsize count = in.gcount();process(buffer, count);
}
else if (in.fail()) {cerr << "文件读取错误" << endl;
}
6、状态转换机制
1. 状态转换图(简易版)

2. 操作对状态的影响
| 操作 | 可能设置的状态位 |
|---|---|
| 打开文件失败 | failbit |
| 读取到文件末尾 | eofbit |
| 类型不匹配 | failbit |
| 流缓冲区损坏 | badbit |
seekg越界 | failbit |
7、调试
1. 输出流状态
void debug_stream_state(const istream& is) {ios::iostate state = is.rdstate();cout << bitset<4>(state) << " : ";if (state & ios::badbit) cout << "bad ";if (state & ios::failbit) cout << "fail ";if (state & ios::eofbit) cout << "eof ";if (state == ios::goodbit) cout << "good";cout << endl;
}
2. 结合gdb调试
(gdb) p cout.rdstate()
$1 = std::_Ios_Iostate = 0
(gdb) p cerr.rdstate()
$2 = std::_Ios_Iostate = 4
8、实践
- 输入后必检状态:所有
operator>>操作后检查流状态 - 优先使用
getline:避免>>在行末遗留\n导致状态混乱 - 异常慎用原则:文件打开等关键操作使用异常,常规错误使用状态检测
- RAII管理状态:利用作用域自动清除临时状态
{ios::fmtflags old_flags = cout.flags(); // 保存原状态cout << hex << 42; // 临时修改// 自动作用域结束恢复 }
关键要点:始终假设IO操作可能失败,并在代码中显式处理所有可能的错误路径。
十、文件的输入和输出
1、基础
1. 包含头文件
首先,需要包含 <fstream> 头文件,该头文件定义了文件流类:
#include <fstream>
#include <string> // 用于字符串操作
#include <limits> // 用于numeric_limits
using namespace std;
2. 文件流类
-
ofstream:输出文件流,用于写入文件。 -
ifstream:输入文件流,用于读取文件。 -
fstream:多功能文件流,支持读写操作。
文件流对象生命周期
1. 对象创建与文件绑定
// 方式1:构造时打开文件 ofstream out("data.txt", ios::app); // 追加模式// 方式2:默认构造+后续打开 ifstream in; in.open("config.ini", ios::in | ios::binary);2. 对象销毁
{fstream tmp("temp.data"); // 进入作用域时打开文件// 操作文件... } // 离开作用域时自动调用close()
2、写入文件(输出操作)
文件操作
方法
功能描述
open(filename, mode)打开文件
is_open()检查文件是否成功打开
close()关闭文件并刷新缓冲区
步骤示例:
// 创建输出文件流对象并打开文件
ofstream outFile("example.txt", ios::out); // ios::out 可省略(默认模式)// 检查是否成功打开
if (!outFile.is_open()) {cerr << "无法打开文件!" << endl;return 1;
}// 写入内容
outFile << "第一行内容" << endl;
outFile << "第二行内容\n";
outFile << 42 << " " << 3.14 << endl;// 显式关闭文件
outFile.close();
打开模式(ios 标志位):
| 模式 | 描述 |
|---|---|
ios::out | 写入模式(默认,覆盖已有内容) |
ios::app | 追加模式(在文件末尾添加内容) |
ios::binary | 二进制模式(非文本文件操作) |
ios::trunc | 清空文件内容(默认与ios::out共用) |
3、读取文件(输入操作)
步骤示例:
// 创建输入文件流对象并打开文件
ifstream inFile("example.txt");// 检查是否成功打开
if (!inFile) { // 等价于 inFile.fail()cerr << "无法打开文件!" << endl;return 1;
}// 逐行读取内容
string line;
while (getline(inFile, line)) {cout << line << endl;
}// 重置错误状态并关闭文件
inFile.clear(); // 清除可能的EOF或错误标志
inFile.close();
格式化读取示例:
int num;
double pi;
if (inFile >> num >> pi) {cout << "读取到: " << num << ", " << pi << endl;
} else {cerr << "格式错误!" << endl;inFile.clear();inFile.ignore(numeric_limits<streamsize>::max(), '\n'); // 跳过错误行
}
4、二进制文件操作
写入二进制数据:
struct Data {int id;char name[20];
};Data data = {1, "Alice"};
ofstream binOut("data.bin", ios::binary);
binOut.write(reinterpret_cast<char*>(&data), sizeof(Data));
binOut.close();
读取二进制数据:
Data data;
ifstream binIn("data.bin", ios::binary);
binIn.read(reinterpret_cast<char*>(&data), sizeof(Data));
cout << "ID: " << data.id << ", Name: " << data.name << endl;
binIn.close();
5、文件指针操作
随机访问文件:
fstream file("data.txt", ios::in | ios::out);// 获取当前读取指针位置
streampos pos = file.tellg();// 移动读取指针到第100字节
file.seekg(100, ios::beg);// 移动写入指针到文件末尾
file.seekp(0, ios::end);// 在文件末尾追加内容
file << "\n追加的内容";file.close();
指针定位常量:
| 常量 | 描述 |
|---|---|
ios::beg | 文件开头(默认) |
ios::end | 文件末尾 |
ios::cur | 当前位置 |
6、错误处理与状态管理
检测流状态:
ifstream file("data.txt");if (file.rdstate() == ios::eofbit) {cout << "到达文件末尾" << endl;
} else if (file.rdstate() & ios::failbit) {cerr << "逻辑错误(如类型不匹配)" << endl;
} else if (file.rdstate() & ios::badbit) {cerr << "物理错误(如文件损坏)" << endl;
}file.close();
启用异常处理:
ifstream file;
file.exceptions(ios::failbit | ios::badbit); // 设置触发异常的标志try {file.open("data.txt");// 文件操作...
} catch (const ios::failure& e) {cerr << "IO异常: " << e.what() << endl;
}
7、实践总结
-
RAII原则:利用对象生命周期自动管理资源。
{ // 自动关闭文件ofstream tmpFile("temp.txt");tmpFile << "临时数据"; } -
路径处理:使用原始字符串或正斜杠避免转义
ofstream log(R"(C:\Users\Logs\app.log)"); // Windows路径 ofstream config("/etc/app/config.conf"); // Linux路径 -
性能优化:
// 减少缓冲区刷新次数 ofstream log("debug.log"); log << unitbuf; // 自动刷新(慎用) -
跨平台换行符:
ofstream out("data.txt"); out << "Line1\r\nLine2"; // Windows换行 out << "Line3\n"; // Unix/Linux换行
十一、string流
1. 头文件包含
#include <sstream> // 包含字符串流相关类
#include <string>
2. 三类字符串流
-
istringstream:从字符串读取数据(输入流)
-
ostringstream:向字符串写入数据(输出流)
-
stringstream:同时支持读写操作(输入输出流)
3. istringstream 使用步骤
场景:将字符串解析为其他类型数据
// 创建输入字符串流并绑定字符串
std::istringstream iss("42 3.14 hello");int num;
double pi;
std::string text;// 像使用cin一样提取数据
iss >> num >> pi >> text;// 验证结果
std::cout << num << " " << pi << " " << text; // 输出:42 3.14 hello
4. ostringstream 使用步骤
场景:将多种类型数据拼接成字符串
std::ostringstream oss;int age = 25;
double score = 95.5;// 像使用cout一样插入数据
oss << "Age: " << age << ", Score: " << score;// 获取最终字符串
std::string result = oss.str();
std::cout << result; // 输出:Age: 25, Score: 95.5
5. stringstream 综合使用
场景:多次读写字符串内容
std::stringstream ss;// 写入数据
ss << "PI: " << 3.14159;// 读取数据
std::string prefix;
double value;
ss >> prefix >> value;// 清空流(重要!)
ss.str(""); // 清空内容
ss.clear(); // 清除错误状态// 重新使用
ss << "New data: " << 100;
std::cout << ss.str(); // 输出:New data: 100
6. 关键方法
| 方法 | 作用 |
|---|---|
str() | 获取/设置底层字符串 |
str("content") | 重置流内容 |
clear() | 清除错误状态(重要!) |
7. 典型应用场景
-
数据格式化:将多个变量组合成格式化的字符串
-
字符串解析:拆分包含混合数据的字符串(如CSV解析)
-
类型转换:实现字符串与其他类型的相互转换
// 字符串转数字
std::istringstream("123") >> number;// 数字转字符串
std::ostringstream() << 456).str();
安全类型转换
template <typename T>
T string_to(const string& s) {istringstream iss(s);T value;if (!(iss >> value >> ws).eof()) {throw invalid_argument("无效转换");}return value;
}auto num = string_to<int>("123"); // 成功返回123
auto d = string_to<double>("abc"); // 抛出异常
复杂字符串解析
void parse_csv(const string& line) {istringstream iss(line);string token;while (getline(iss, token, ',')) {// 处理每个CSV字段process_field(token);}
}
动态字符串构建
string build_sql(const string& table, int id) {ostringstream oss;oss << "SELECT * FROM " << quoted(table) << " WHERE id=" << id << ";";return oss.str(); // 自动处理SQL注入问题
}
重复使用字符串流
ostringstream oss;
oss << "第一部分";
cout << oss.str(); // 输出"第一部分"oss.str(""); // 清空内容
oss << "新内容"; // 重新使用
处理带引号的字符串
string input = "\"Hello, World!\" 42";
istringstream iss(input);
string text;
int num;iss >> quoted(text) >> num; // text="Hello, World!", num=42
二进制数据转换
vector<uint8_t> bytes = {0x48, 0x65, 0x6c, 0x6c, 0x6f};
ostringstream oss;
oss.write(reinterpret_cast<char*>(bytes.data()), bytes.size());
string binary_str = oss.str();
8. 注意事项
-
状态管理:多次使用同一个流时需要调用
clear()和str("") -
错误处理:检查流状态是否有效
if (!(iss >> value)) {std::cerr << "格式错误!";
}
-
性能:频繁创建流对象会影响性能,可复用对象
9.性能优化
1. 预分配缓冲区
ostringstream oss;
oss.rdbuf()->pubsetbuf(nullptr, 1024); // 预分配1KB缓冲区
2. 减少临时对象
// 低效方式
string s1 = oss1.str() + oss2.str();// 高效方式
ostringstream final;
final << oss1.rdbuf() << oss2.rdbuf();
string result = final.str();
十二、扩展与自定义
1. 重载操作符实现自定义类型IO
struct Vec3 {float x, y, z;friend ostream& operator<<(ostream& os, const Vec3& v) {return os << "[" << v.x << "," << v.y << "," << v.z << "]";}friend istream& operator>>(istream& is, Vec3& v) {char dummy;return is >> dummy >> v.x >> dummy >> v.y >> dummy >> v.z >> dummy;}
};// 使用示例
Vec3 v;
cin >> v; // 输入格式示例:[1,2,3]
cout << v; // 输出:[1,2,3]
2. 创建自定义流
class TeeStream : public ostream {ostream& stream1;ostream& stream2;TeeStream(ostream& s1, ostream& s2) : ostream(s1.rdbuf()), stream1(s1), stream2(s2) {}TeeStream& operator<<(ostream& (*manip)(ostream&)) {stream1 << manip;stream2 << manip;return *this;}// 需要重载其他<<操作符...
};// 同时输出到控制台和文件
ofstream logfile("log.txt");
TeeStream mycout(cout, logfile);
mycout << "同时输出到两个目标" << endl;
相关文章:
C++:IO库
一、C IO库的架构 C标准库中的IO系统基于流(Stream)的概念,分为三层结构: 流对象(如cin, cout, fstream)流缓冲区(streambuf,负责底层数据处理)数据源/目的…...
企业级前端架构设计与实战
一、架构设计核心原则 1.1 模块化分层架构 典型目录结构: src/├── assets/ # 静态资源├── components/ # 通用组件├── pages/ # 页面模块├── services/ # API服务层├── store/ # 全局状态管理├── uti…...
从入门到精通【MySQL】 CRUD
文章目录 📕1. Create 新增✏️1.1 单行数据全列插入✏️1.2 单行数据指定列插入✏️1.3 多行数据指定列插入 📕2. Retrieve 检索✏️2.1 全列查询✏️2.2 指定列查询✏️2.3 查询字段为表达式✏️2.4 为查询结果指定别名✏️2.5 结果去重查询 …...
08_双向循环神经网络
双向网络 概念 双向循环神经网络(Bidirectional Recurrent Neural Network, BiRNN)通过同时捕捉序列的正向和反向依赖关系,增强模型对上下文的理解能力。与传统的单向网络不同,BIRNN 能够同时从过去和未来的上下文信息中学习,从而提升模型的…...
JSON数据修改的实现
JSON数据的修改 示例代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; //C#命名空间(以System开头) using System.IO; using LitJson; public class JsonChange : MonoBehaviour {// Start is called befor…...
2025年Postman的五大替代工具
虽然Postman是一个广泛使用的API测试工具,但许多用户在使用过程中会遇到各种限制和不便。因此,可能需要探索替代解决方案。本文介绍了10款强大的替代工具,它们能够有效替代Postman,成为你API测试工具箱的一部分。 什么是Postman&…...
(四)---四元数的基础知识-(定义)-(乘法)-(逆)-(退化到二维复平面)-(四元数乘法的导数)
使用四元数的原因 最重要的原因是因为传感器的角速度计得到的是三个轴的角速度, 这三个轴的角速度合成一个角速度矢量, 结果就是在微小时间内绕着这个角速度矢量方向为轴旋转一定角度. 截图来源网址四元数 | Crazepony开源四轴飞行器...
汇能感知高品质的多光谱相机VSC02UA
VSC02UA概要 VSC02UA是一款高品质的200万像素的光谱相机,适用于工业检测、农业、医疗等领域。VSC02UA 包含 1600 行1200 列有源像素阵列、片上 10 位 ADC 和图像信号处理器。它带有 USB2.0 接口,配合专门的电脑上位机软件使用,可进行图像采集…...
【SpringBoot】MorningBox小程序的完整后端接口文档
以下是「晨光宅配」小程序的完整接口文档,涵盖了所有12个表的接口。 每个接口包括请求方法、URL、请求参数、响应格式和示例 接口文档 1. 用户模块 1.1 获取用户信息 URL: /user/{userId}方法: GET请求参数: userId (路径参数): 用户ID响应格式:{"userId": 1,&qu…...
Blazor+PWA技术打造全平台音乐播放器-从音频缓存到离线播放的实践之路
开局三张图… 0.起源 主要是自己现在用的是苹果手机,虽然手机很高级,但是想听自己喜欢的歌曲确是不容易,在线app都要付费,免费的本地播放器都不太好用(收费的也不太行),基础功能都不满足。此外…...
使用LangChain开发智能问答系统
代码地址见文末 1. 项目配置 1.1 Neo4j 数据库配置 1. 安装与环境变量 解压路径:将neo4j-community-5.x.x.zip解压至D:\neo4j-community-5.x.x环境变量: NEO4J_HOME: D:\neo4j-community-5.x.xJAVA_HOME: D:\neo4j-community-5.x.x\jdk(注意:需指向 JDK 目录)Path 变量…...
Centos操作系统安装及优化
Centos操作系统安装及优化 零、环境概述 主机名 centos版本 cpu 内存 Vmware版本 ip地址 test CentOS Linux release 7.6.1810 (Core) 2C 2G 15.5.1 10.0.0.10 一、介质下载 1、7.6版本下载 CentOS7.6标准版下载链接: https://archive.kernel.org/centos-vault/7.6.1810/i…...
游戏引擎学习第177天
仓库:https://gitee.com/mrxiao_com/2d_game_4 今日计划 调试代码有时可能会非常困难,尤其是在面对那些难以发现的 bug 时。显然,调试工具是其中一个非常重要的工具,但在游戏开发中,另一个非常常见的工具就是自定义的调试工具&a…...
springCloud集成tdengine(原生和mapper方式) 其一
第一种 mapper方式,原生方式在主页看第二章 一、添加pom文件 <!-- HikariCP 连接池 --><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></dependency><!-- TDengine 连接器--><de…...
数据结构知识点1
目录 一、时间复杂度和空间复杂度 1.1时间复杂度: 1.2空间复杂度: 二、装箱和拆箱 三、泛型 3.1泛型类的使用: 3.2泛型的上界: 3.3泛型方法: 一、时间复杂度和空间复杂度 1.1时间复杂度: 时间复杂…...
时序数据库QuestDB在Winform窗体应用
以下是QuestDB在Winform使用的代码: //初始化 private void Init() { //创建数据库对象 (用法和EF Dappper一样通过new保证线程安全) SqlSugarClient Db new SqlSugarClient(new ConnectionConfig() { ConnectionString “host10.3.5.227;port8812;usernameadmin;…...
从零开始学习 Go 语言
Go 语言(又称 Golang)是由 Google 开发的一种静态强类型、编译型、并发型编程语言。它以其简洁的语法、高效的并发支持和强大的标准库而闻名,非常适合开发高性能的服务器端应用、分布式系统和云计算工具。本文将从零开始,详细介绍…...
自由学习记录(45)
顶点片元着色器(important) 1.需要在Pass渲染通道中编写着色器逻辑 2.可以使用cG或HLSL两种shader语言去编写Shader逻辑 3.代码量较多,灵活性较强,性能消耗更可控,可以实现更多渲染细节 4.适用于光照处理较少…...
数据源支持远程Excel/CSV,数据集支持分组字段功能,DataEase开源BI工具v2.10.6 LTS版本发布
2025年3月17日,人人可用的开源BI工具DataEase正式发布v2.10.6 LTS版本。 这一版本的功能变动包括:数据源方面,新增支持远程Excel/CSV数据源,支持以HTTP、HTTPS、FTP协议获取远程服务器上的Excel和CSV数据文件,并且可以…...
SpringBoot3使用CompletableFuture时java.util.ConcurrentModificationException异常解决方案
问题描述 在Spring Boot 3项目中,使用CompletableFuture进行异步编程时,偶发{"code":500,"msg":"java.util.ConcurrentModificationException"}异常,但代码中并未直接操作List或CopyOnWriteArrayList等集合类…...
【Nodejs】2024 汇总现状
之前已经调研了容器、nexus-public,实现了本地构建应用镜像和基础设施的镜像。为实现分布式一体化协作开发的目标,还需要配套的线上协作开发环境。故而重回前端调研现状,比较 5 年前的 nodejs 快好的啊。 以下是针对 Node.js 工具链的深度解析…...
LeetCode 每日一题 2025/3/17-2025/3/23
记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步 目录 3/17 1963. 使字符串平衡的最小交换次数3/18 2614. 对角线上的质数3/19 2610. 转换二维数组3/20 2612. 最少翻转操作数3/21 2680. 最大或值3/22 2643. 一最多的行3/23 2116…...
STM32__红外避障模块的使用
目录 一、红外避障模块 概述 二、直接读取OUT引脚电平 三、使用中断方式触发 一、红外避障模块 概述 引脚解释: VCC接3.3V 或 5.0VGND接开发板的GNDOUT数字量输出(0或1); 低电平时表示前方有障碍 ; 通过可调电阻调整检测距离 产品特点: …...
android 音量调节
安卓音频数据的最终音量由三部分组成,分别是master volume(全局音量,对整个系统所有的音频数据生效),stream volume(流音量,只针对特定类型的音频数据生效)和track volume(track音量,只针对某个audiotrack的…...
Redis JSON 用id读取content总结(sendCommand())
Redis JSON 读取总结(方法 2 - sendCommand()) 💡 背景 在 Redis 中,我们存储了 JSON 数据,并希望通过 Jedis sendCommand() 方式读取 JSON 里的 "content" 字段。由于 jedis.jsonGet() 可能在旧版本不支持…...
使用Qdrant等其他向量数据库时需要将将numpy 数组转换为列表 确保数据能被正确处理和序列化,避免类型不兼容的问题。
在使用Qdrant等其他向量数据库时需要 转换 numpy 数组为列表主要是为了确保数据能被正确处理和序列化,避免类型不兼容的问题。具体原因如下: 序列化兼容性: 很多数据库接口、API 或者 JSON 序列化工具只能处理 Python 的内置类型(…...
[AI速读]如何构建高效的AMBA协议检查器(Checker IP)
在芯片验证过程中,检查器(Checker)是确保设计符合协议规范的关键工具。本文基于一篇技术论文,分享如何为AMBA协议(如AXI、AHB)构建可重用的检查器IP(Checker IP,简称CIP),并简化其核心思路,帮助工程师快速上手。 一、什么是Checker IP? Checker IP是一组用SystemVe…...
基于3DMax与Vray引擎的轻量级室内场景渲染实践
欢迎踏入3DMAX室内渲染的沉浸式学习之旅!在这个精心设计的实战教程中,我们将携手揭开3DMAX与Vray这对黄金搭档在打造现实室内场景时的核心奥秘。无论您是渴望入门的3D新手,还是追求极致效果的专业设计师,这里都将为您呈现从场景蓝图构建到光影魔法施加的完整技术图谱。我们…...
浅谈Qt事件子系统——以可拖动的通用Widget为例子
浅谈Qt事件子系统——以可拖动的通用Widget为例子 这一篇文章是一个通过实现可拖动的通用Widget为引子简单介绍一下我们的事件对象子系统的事情 代码和所有的文档 1:Qt侧的API介绍和说明 这个是每一个小项目的惯例,我会介绍大部分Qt程序中使用到的…...
QT Quick(C++)跨平台应用程序项目实战教程 2 — 环境搭建和项目创建
目录 引言 1. 安装Qt开发环境 1.1 下载Qt安装包 1.2 安装Qt 1.3 安装MSVC编译器 2. 创建Qt Quick项目 2.1 创建新项目 2.2 项目结构 2.3 运行项目 3. 理解项目代码 3.1 main.cpp文件 3.2 Main.qml文件 引言 在上一篇文章中,我们介绍了本教程的目标和结…...
