【C++】《C++ 的 IO 流》---详解
2026/9/16 9:47:52 网站建设 项目流程

学完 C 语言的 scanf / printf,再来看 C++ 的 cin / cout,你可能觉得"不就是换了个写法吗"。其实 C++ 把输入输出抽象成了"流",还提供了文件流、字符串流等一整套工具。本文从"流是什么"讲起,一路讲到 OJ 输入技巧、文件读写、stringstream,所有代码都在 g++ (C++17) 上实际运行验证过,照着抄就能跑。


目录

  1. 先回顾:C 语言的输入与输出

  2. "流"到底是什么

  3. C++ IO 流类家族

  4. 标准 IO 流:cin / cout / cerr / clog

  5. OJ 刷题输入输出专题

  6. 为什么 while(cin >> x) 能当循环条件

  7. 文件 IO 流

  8. stringstream:把字符串当成流来用

  9. 全文总结


1. 先回顾:C 语言的输入与输出

C 语言最常用的两个函数:

  • scanf():从键盘读取数据,存入变量;

  • printf():把数据格式化后输出到屏幕,重点掌握宽度控制精度控制(比如%5d%.2f)。

不管是输入还是输出,C 语言在程序和硬件之间都加了一层缓冲区

键盘 ──► 输入缓冲区 ──► 程序 ──► 输出缓冲区 ──► 显示器

缓冲区有两个好处,用大白话讲:

  1. 屏蔽底层差异:数据从哪来、到哪去(键盘、文件、网卡……),底层操作完全不同。缓冲区把这些脏活累活统一包起来,同一套scanf/printf代码换个平台也能跑,可移植性好;

  2. 攒一批再处理,效率高:直接操作硬件很慢,缓冲区先把数据攒起来,一次读/写一批。另外计算机本身没有"行"的概念,有了缓冲区,我们才能定义"读到换行符就算一行"这样的规则。

C++ 的流继承了缓冲思想,所以理解缓冲区对后面很重要。


2. "流"到底是什么

流(stream),顾名思义就是"流动"——数据从一个地方流向另一个地方。它是对"有序、连续、带方向"的数据的一种抽象。

  • 数据从键盘流向内存(变量),叫输入

  • 数据从内存流向显示器/文件,叫输出

可以把流想象成一根水管:数据是水,>>是把水从管里抽进变量,<<是把变量里的水灌进管子。

C++ 提供了一整套 I/O 标准类库,里面的每个类都叫"流类",分别负责控制台、文件、字符串等不同场景。


3. C++ IO 流类家族

C++ 实现了一个庞大的流类库,继承关系简化后是这样:

ios_base(格式、状态等最基础功能) └── ios(持有一个 streambuf,streambuf 才是真正存数据的缓冲区) ├── istream(输入流:负责"读",cin 就是它的对象) ├── ostream(输出流:负责"写",cout/cerr/clog 是它的对象) └── iostream(多重继承 istream + ostream,既能读又能写)

按用途分,常用的有三大类:

头文件

场景

常用类

<iostream>

控制台输入输出

cin/cout/cerr/clog

<fstream>

文件读写

ifstream(读文件)、ofstream(写文件)、fstream(读写)

<sstream>

把字符串当流用

istringstreamostringstreamstringstream

一句话总结:istream管读,ostream管写,streambuf管缓冲区,所有流都是同一套家族里的成员,所以它们的用法惊人地一致——都是>><<


4. 标准 IO 流:cin / cout / cerr / clog

C++ 标准库内置了四个全局流对象,程序一启动就能用:

对象

类型

作用

是否缓冲

cin

istream

标准输入,默认从键盘读

缓冲

cout

ostream

标准输出,输出到显示器

缓冲

cerr

ostream

标准错误,输出错误信息

不缓冲,立刻显示

clog

ostream

标准错误,输出日志信息

缓冲

小知识:cerrclog都输出到标准错误(stderr),区别是 cerr 不缓冲——程序崩之前错误信息也一定能刷到屏幕上;clog 带缓冲,适合量大的日志。

使用它们要包含<iostream>,并引入命名空间(后面的代码统一using namespace std;)。


关于 cin 的 5 个注意点

① cin 是缓冲流。键盘敲的内容先进缓冲区,cin >>直接从缓冲区取;一次输入太多,用不完的数据会留在缓冲区里给下次用。

② 类型不匹配不会让程序崩溃,但流会"坏掉"。比如变量是 int,你却输入字母,流会进入错误状态(failbit),之后所有输入语句全部跳过。需要手动用cin.clear()恢复状态、cin.ignore()清掉脏数据,这属于进阶内容,初学先有个印象。

③ 默认以空格、Tab、换行作为分隔符。所以cin >> s读字符串时遇到空格就停,读不了带空格的一整行(读整行要用getline,后面讲)。

④ cin/cout 能直接处理 int、double、char 等内置类型,是因为标准库已经为这些类型重载好了>><<运算符。

⑤ 自定义类型不能直接 cin/cout。想写cout << 对象,就得自己重载<<;想写cin >> 对象,就得自己重载>>

#include <iostream> #include <string> using namespace std; struct Student { int id; string name; }; // 重载 << :左边必须是 ostream,所以不能写成 Student 的成员函数,只能写在全局 ostream& operator<<(ostream& os, const Student& s) { os << s.id << " " << s.name; return os; // 返回流对象,才能支持 cout << a << b 链式调用 } // 重载 >> :参数是非 const 引用,因为要往里写数据 istream& operator>>(istream& is, Student& s) { is >> s.id >> s.name; return is; } int main() { Student st; cin >> st; // 可以直接输入整个对象 cout << st << '\n'; return 0; }

运行示例:

补充一个暖心细节:为什么cout << "请输入:"后面不写endl,提示语也能立刻显示?因为cin 和 cout 默认绑在一起,cin 读数据前会自动把 cout 缓冲区刷出去,不用我们操心。


5. OJ 刷题输入输出专题

刷算法题时,逻辑全对但输入输出格式写错导致 WA(答案错误)是最冤的。这一节把几种固定模板讲清楚。


5.1 最重要的概念:EOF

OJ 会把测试数据一次性喂给程序,数据读完就到了文件末尾,即EOF(End Of File)while (cin >> x)读到 EOF 时循环条件为假,自动结束——不用我们自己写结束标记

  • Windows / VS本地调试:输入完数据后按Ctrl + Z再回车,手动模拟 EOF;

  • Linux / Mac(包括云端虚拟机):按Ctrl + D

  • 在线提交:什么都不用做,系统自动处理。

另外,输出格式必须和题目描述逐字符一致:该空格的地方空格,该换行的地方换行,多一个少一个都可能 WA。


5.2 模板一:循环读单个数据

int a; while (cin >> a) { // 处理 a }

原理:cin >> a自动跳过空格、Tab、换行,只抓有效数据;读到数据就继续循环,读到 EOF 就停止。

适用场景:多组测试数据,不告诉你有多少组,每组一个数(或一个不带空格的字符串)。


5.3 模板二:每组读多个数据

int a, b, c; while (cin >> a >> b >> c) { // 一次读一组的三个数 }

数据之间用空格还是换行隔开都可以,cin 会自动忽略空白。

注意区分另一种题型:题目第一行先给一个 n,表示后面恰好有 n 组数据。这时不能用 EOF 循环,要用 for 循环固定读 n 次:

int n; cin >> n; for (int i = 0; i < n; ++i) { int a, b; cin >> a >> b; // 处理 }

两种写法千万别写混。比如题目第一行给组数2,后面是两组数据,你却用while (cin >> a >> b)读 EOF,实测会把组数2也当成数据:读成(2,10)(20,30),最后一个数40直接丢失,答案全部错位 WA(本地交互式调试时还可能表现为程序一直等输入)。


5.4 模板三:读取一整行(极易踩坑)

cin >>碰到空格就停。要保留空格、读取完整一行,用getline

string line; while (getline(cin, line)) { // line 里就是完整的一行(不含行末的换行符本身) }

经典大坑:cin 和 getline 混用

int n; cin >> n; // 输入 3 后按回车,数字 3 被取走,换行符 '\n' 还留在缓冲区 string s; getline(cin, s); // getline 一上来就碰到这个残留的 '\n',读到一个空字符串!

解决方法:在 getline 前把残留的换行吃掉。

int n; cin >> n; cin.ignore(); // 忽略 1 个字符(正好是那个 '\n') // 更保险的写法:一直忽略到换行符为止 // cin.ignore(1024, '\n'); string s; getline(cin, s); // 这次正常读到一整行

5.5 模板怎么选

输入特征

选择

一个个数字/单词,空白分隔,不需要保留空格

while (cin >> x)

必须拿到带空格的一整行

while (getline(cin, line))

第一行明确给了组数 n

for循环读 n 次


5.6 输出的实战细节

  1. 少用endl,多用'\n'endl每输出一次都强制刷新缓冲区,数据量大时频繁刷盘会超时;'\n'只换行不刷新,快得多。

  2. 题目要求"每组答案占一行",正常在每组末尾输出一个'\n'即可;个别判题严格的 OJ 不喜欢末尾多余空行,拿不准时保持"每组恰好一个换行"。

  3. 题目要求两个数字之间用空格分隔时,行尾不要多加空格,这是高频扣分点。


6. 为什么 while(cin >> x) 能当循环条件

cin >> x调用的是operator>>,它的返回值是istream 流对象本身(所以才能cin >> a >> b链式调用)。一个对象怎么能放进 if / while 的条件里呢?

因为 istream 提供了一个类型转换运算符

explicit operator bool() const; // 把流对象转换成 bool
  • 流状态正常 → 转换为true

  • 读到 EOF 或类型不匹配(流进入 fail 状态)→ 转换为false

所以while (cin >> x)的完整含义是:读一个数据,然后检查流还好不好,好就进循环,不好就结束。下面用自定义的 Date 类完整演示:

#include <iostream> using namespace std; class Date { friend ostream& operator<<(ostream& out, const Date& d); friend istream& operator>>(istream& in, Date& d); public: Date(int year = 1, int month = 1, int day = 1) : _year(year), _month(month), _day(day) {} private: int _year; int _month; int _day; }; istream& operator>>(istream& in, Date& d) { in >> d._year >> d._month >> d._day; return in; } ostream& operator<<(ostream& out, const Date& d) { out << d._year << " " << d._month << " " << d._day; return out; } int main() { Date d; while (cin >> d) // 读到 EOF 自动结束,OJ 多组输入首选写法 { cout << d << '\n'; } return 0; }

7. 文件 IO 流

文件按内容形式分为文本文件(人能看懂的字符)和二进制文件(字节原样存储)。C++ 用文件流操作文件,固定四步:

  1. 定义文件流对象

    • ifstream ifile;—— 只读;

    • ofstream ofile;—— 只写;

    • fstream iofile;—— 可读可写。

  2. open 打开文件,把流对象和磁盘文件关联起来,打开后一定要判断是否成功

  3. 读写:文本文件用>>/<<,二进制文件用read()/write()成员函数;

  4. close 关闭文件(对象析构时也会自动关,但建议显式调用,代码更清晰)。


7.1 常用打开模式

模式

含义

ios::in

只读打开

ios::out

只写打开,默认会清空(截断)原文件内容(实测ofstreamfstream只要单独用ios::out都会清空;想保留旧内容要用fstream+ios::in | ios::out

ios::app

追加写入,数据加到文件末尾,不覆盖旧内容

ios::binary

二进制方式打开

多种模式可以用|组合,例如ios::out | ios::binary


7.2 四个易错点

  1. 默认是文本模式,要读写二进制必须显式加ios::binary

  2. ofstream默认打开就清空文件,想追加内容要用ios::app

  3. while (ifile >> x)读到文件末尾自动结束,原理和while (cin >> x)完全一样;

  4. 文件流对象销毁时会自动 close,但手动 close 是更好的习惯。


7.3 入门 Demo:文件不存在就创建,存在就读数字

#include <fstream> #include <iostream> using namespace std; int main() { ifstream ifile("test.txt"); if (!ifile) // 打开失败(文件不存在或没权限) { cout << "文件不存在,正在创建...\n"; ofstream ofile("test.txt"); if (ofile.is_open()) { ofile << "10 20 30 40 50\n"; ofile.close(); cout << "文件已创建,请重新运行程序读取数据。\n"; } return 0; } int a; while (ifile >> a) { cout << a << " "; } cout << '\n'; ifile.close(); return 0; }

7.4 进阶 Demo:配置信息的二进制读写与文本读写

#include <iostream> #include <fstream> #include <string> #include <cstring> using namespace std; class Date { public: int _year, _month, _day; Date() : _year(0), _month(0), _day(0) {} Date(int y, int m, int d) : _year(y), _month(m), _day(d) {} friend ostream& operator<<(ostream& out, const Date& d); friend istream& operator>>(istream& in, Date& d); }; ostream& operator<<(ostream& out, const Date& d) { out << d._year << "-" << d._month << "-" << d._day; return out; } istream& operator>>(istream& in, Date& d) { // 文本格式固定为 2026-9-5,用两个字符变量吃掉分隔符 '-' char ch1, ch2; in >> d._year >> ch1 >> d._month >> ch2 >> d._day; return in; } struct ServerInfo { char _address[32]; int _port; Date _date; ServerInfo() : _port(0) { memset(_address, 0, sizeof(_address)); } ServerInfo(const char* address, int port, const Date& date) : _port(port), _date(date) { // strncpy 是跨平台的;strcpy_s 是 VS 提供的安全版本,g++ 默认不认识 strncpy(_address, address, sizeof(_address) - 1); } }; class ConfigManager { public: ConfigManager(const char* filename) : _filename(filename) {} void WriteBin(const ServerInfo& info) { ofstream ofs(_filename, ios::out | ios::binary); if (!ofs.is_open()) { cerr << "二进制文件打开失败(写入)\n"; return; } ofs.write((const char*)&info, sizeof(info)); } void ReadBin(ServerInfo& info) { ifstream ifs(_filename, ios::in | ios::binary); if (!ifs.is_open()) { cerr << "二进制文件打开失败(读取)\n"; return; } ifs.read((char*)&info, sizeof(info)); } void WriteText(const ServerInfo& info) { ofstream ofs(_filename); if (!ofs.is_open()) { cerr << "文本文件打开失败(写入)\n"; return; } ofs << info._address << " " << info._port << " " << info._date; } void ReadText(ServerInfo& info) { ifstream ifs(_filename); if (!ifs.is_open()) { cerr << "文本文件打开失败(读取)\n"; return; } ifs >> info._address >> info._port >> info._date; } private: string _filename; }; int main() { ServerInfo winfo("192.168.0.1", 80, Date(2026, 9, 5)); ConfigManager cf_bin("test.bin"); cf_bin.WriteBin(winfo); ServerInfo rbinfo; cf_bin.ReadBin(rbinfo); cout << "二进制读回: " << rbinfo._address << " " << rbinfo._port << " " << rbinfo._date << '\n'; ConfigManager cf_text("test.txt"); cf_text.WriteText(winfo); ServerInfo rtinfo; cf_text.ReadText(rtinfo); cout << "文本读回: " << rtinfo._address << " " << rtinfo._port << " " << rtinfo._date << '\n'; return 0; }

运行结果(本机实测):

生成的 test.txt 用记事本可以直接看懂:192.168.0.1 80 2026-9-5;test.bin 则是一串"乱码"字节。


7.5 关于这段代码的 5 个关键理解

① 为什么 Date 要重载 << 和 >>?

编译器只认识内置类型。ofs << info._date里 Date 是自定义类型,你不告诉它"年月日按什么格式输出",它就不知道怎么办。

② 为什么重载函数必须写成全局函数?

cout << d等价于operator<<(cout, d),其左操作数是 ostream 对象,而非 Date。由于成员函数的左操作数必须是所属类自身,因此该重载只能写成全局函数;若需访问私有成员,可借助友元声明。

③ 二进制读写的本质是"内存原样拷贝"

ofs.write((const char*)&info, sizeof(info)); // 把结构体所占内存一字节不差地写进文件 ifs.read((char*)&info, sizeof(info)); // 读回来重新盖到同类型结构体的内存上

④ 二进制读写有两个大坑:

  • 结构体里不能有指针char*std::string内部都有指针)。存指针存的是当前进程的内存地址,下次运行地址早就失效了;

  • 结构体对齐填充字节,而且不同机器/编译器的对齐方式、大小端可能不同,所以二进制文件跨平台有隐患。只适合"全是值类型(int、char 数组、嵌套值类型对象)且在同一环境读写"的场景。

⑤ 二进制 vs 文本怎么选?

方式

核心操作

优点

缺点

二进制

write/read 整块内存拷贝

速度快、代码简单

人看不懂,含指针的结构体不能用,跨平台有隐患

文本

<</>>逐字段转换

人能直接看懂、方便调试、跨平台好

稍慢,自定义类型要重载运算符

这段代码最大的亮点是统一:不管 int、char 数组还是 Date 对象,全用同一套<</>>,自定义类型用起来和内置类型一样自然——这就是运算符重载的价值。


8. stringstream:把字符串当成流来用

8.1 C 语言是怎么做类型转换的

C 语言想把整数转成字符串,常用两个函数:

#include <cstdio> int main() { int n = 123456789; char s[32]; // 方法1:itoa —— 不是标准 C,Windows/VS 上可用(VS 里叫 _itoa_s),Linux 下没有 // _itoa_s(n, s, sizeof(s), 10); // 方法2:snprintf —— 标准 C 函数,跨平台,推荐 snprintf(s, sizeof(s), "%d", n); printf("%s\n", s); return 0; }

函数

优点

缺点

跨平台

itoa/_itoa_s

整数转字符串很直接

不是标准 C,Linux 下没有

snprintf

标准函数,格式灵活

要记%d%f等格式符;缓冲区要给够大小

两个血泪教训:

  1. 缓冲区给小了会溢出123456789是 9 个数字加结尾的\0,共占 10 个字节——char buf[10]恰好够,写少一个字节才会溢出。所以用 snprintf 时第二个参数一定要传sizeof(缓冲区),它超了会截断,不会越界;

  2. 格式符必须和类型匹配%f要配 double,配 int 属于未定义行为,程序可能打印 0、打印乱码,甚至崩溃,编译器也会给警告。

C++ 给出的更省心的方案就是stringstream


8.2 stringstream 三兄弟

包含头文件<sstream>,标准库提供三个类:

用途

istringstream

从字符串里数据(字符串 → 变量)

ostringstream

往字符串里数据(变量 → 字符串)

stringstream

既能读又能写

用法和 cin/cout 一模一样:<<往里写,>>往外读。


8.3 用途一:数值和字符串互转

#include <sstream> #include <string> #include <iostream> using namespace std; int main() { int a = 12345678; string sa; stringstream ss; ss << a; // 像写 cout 一样写进去 ss >> sa; // 像读 cin 一样读出来,sa 就是 "12345678" cout << "int 转 string: " << sa << '\n'; // 复用同一个 ss:清空两件套 ss.clear(); // ① 清状态标志 ss.str(""); // ② 清底层内容 double d = 12.34; ss << d; cout << "double 转 string: " << ss.str() << '\n'; // str() 直接取出整个字符串 return 0; }

运行结果:

两种取出方式的区别:

ss << 123; int n; ss >> n; // 方式①:>> 提取,内容像被 cin 读走一样被"消费" ss << 123; string s = ss.str();// 方式②:str() 直接拷贝一份底层字符串,不影响 ss 的读取位置

8.4 重点中的重点:清空"两件套"

复用同一个 stringstream 时,必须分清它内部有两样独立的东西

  • 状态标志(eofbit / failbit 等):记录"我是不是读完了、有没有出错";

  • 底层字符串:真正存放内容的地方。

ss.clear(); // 只清状态标志,不动内容 ss.str(""); // 只清空内容,不动状态标志

为什么读过之后必须两件套?看实测(g++ C++17):

stringstream ss; string s; ss << 123; ss >> s; // "123" 被读完,流到达末尾,状态被置为 eof ss.str(""); // 只清内容,不清状态 ss << 456; // 写入直接失败!标准要求写入前流必须是"好的"状态 ss.str(); // 结果是空字符串 "",不是 "123456"

注意:这是 C++ 标准规定的确定行为,不是"看编译器心情"。 还要特别纠正一个常见误解:状态是脏的情况下ss << 456根本写不进去,内容不会变成 "123456";"追加成 123456" 只可能发生在状态正常但没清内容时。

记不住规则就按这个表来:

使用情况

怎么清

只用<<写,从没用>>

状态不会脏,ss.str("")清内容即可

用过>>读(到过末尾)

必须clear()+str("")两件套

拿不准 / 懒得记

永远两件套,多写一行不吃亏

只用一次

干脆每次新建一个stringstream ss;,什么都不用清


8.5 用途二:字符串拼接

#include <sstream> #include <iostream> using namespace std; int main() { stringstream ss; ss << "first" << " " << "string,"; ss << " second string"; cout << "拼接结果: " << ss.str() << '\n'; // 拼接结果: first string, second string return 0; }

写入默认是追加不是覆盖,想从头来过就str("")


8.6 用途三:结构体的序列化与反序列化

序列化:把结构体数据按一定格式拼成字符串(方便存文件、发网络);反序列化:收到字符串后再还原回结构体。有了流和运算符重载,这两件事和普通输入输出完全一样。

#include <iostream> #include <sstream> #include <string> using namespace std; class Date { public: int _year, _month, _day; Date(int y = 0, int m = 0, int d = 0) : _year(y), _month(m), _day(d) {} friend ostream& operator<<(ostream& out, const Date& d); friend istream& operator>>(istream& in, Date& d); }; ostream& operator<<(ostream& out, const Date& d) { out << d._year << "-" << d._month << "-" << d._day; return out; } istream& operator>>(istream& in, Date& d) { char ch1, ch2; in >> d._year >> ch1 >> d._month >> ch2 >> d._day; return in; } struct ChatInfo { string _name; // 昵称 int _id; // ID Date _date; // 时间 string _msg; // 消息 }; int main() { ChatInfo winfo = { "hjq", 135246, Date(2026, 9, 5), "晚上一起看电影吧" }; // ① 序列化:结构体 -> 字符串 ostringstream oss; oss << winfo._name << " " << winfo._id << " " << winfo._date << " " << winfo._msg; string str = oss.str(); cout << "序列化后: " << str << '\n'; // ② 模拟通过网络发送 str,对方收到后反序列化:字符串 -> 结构体 ChatInfo rInfo; istringstream iss(str); iss >> rInfo._name >> rInfo._id >> rInfo._date >> rInfo._msg; cout << "还原姓名: " << rInfo._name << " (" << rInfo._id << ")\n"; cout << "还原消息: " << rInfo._name << "> " << rInfo._msg << '\n'; return 0; }

运行结果:

注意这个简单协议的局限:字段之间用空格分隔,所以消息内容里不能带空格(否则"晚上 看电影"会被切成两个字段)。真实项目(JSON、Protobuf 等)会用更完善的格式解决这个问题,学习阶段先理解"拼字符串—拆字符串"的思想即可。

顺便对比一下:ChatInfo 里有 string 成员,所以它不能write((char*)&info, sizeof(info))做二进制序列化(string 内部有指针),只能像这样转成文本。


8.7 stringstream 的底层原理

一句话:stringstream 内部维护了一个 string 对象

  • <<写入:往这个 string 后面追加内容;

  • >>读取:在这个 string 里维护一个读取位置,从头按类型提取;

  • str():返回这个 string 的拷贝;

  • string 会自动扩容,所以不用像 C 语言那样担心 char 数组缓冲区不够。

对比 C 风格写法:

char buf[6]; int ret = snprintf(buf, sizeof(buf), "%d", 123456789); // 缓冲区不够:snprintf 只会截断成 "12345",绝不越界(换成 sprintf 就溢出了) // 返回值 9 表示"本该写 9 个字符",ret >= 缓冲区大小就说明被截断了 // 另外还得自己记住 %d、%f 这些格式符 stringstream ss; ss << 123456789; // 底层 string 自动扩容,多大都不怕 string s = ss.str(); // 自动识别类型,没有格式符不匹配的问题

8.8 使用建议

场景一:一次性转换,直接新建,用完销毁:

string IntToStr(int n) { stringstream ss; // 局部对象,不用操心清空 ss << n; return ss.str(); }

场景二:频繁转换、复用同一个对象,老老实实用两件套:

stringstream ss; ss << 123; string s1 = ss.str(); ss.clear(); ss.str(""); ss << 456; string s2 = ss.str();

补充:单纯做"数字 → string",C++11 以后还可以直接用to_string();"string → 数字"用stoi/stod。它们更轻量;stringstream 的优势在于一次处理多个字段、任意类型混搭,比如 8.6 的结构体序列化。


9. 全文总结

  1. 流是对有序、连续、带方向数据的抽象>>负责输入、<<负责输出,控制台流、文件流、字符串流用法完全一致;

  2. cin/cout处理控制台输入输出,带缓冲;cerr不缓冲用于报错,clog缓冲用于日志;

  3. 自定义类型想直接用 cin/cout,必须重载>><<,且要写成全局函数(左操作数是流对象);

  4. OJ 刷题记住三套模板:单个/多个数据用while(cin >> x),整行用getline,给了组数用for;cin 和 getline 混用时记得ignore()吃掉残留换行;输出少用endl多用'\n'

  5. while(cin >> x)能当条件,靠的是 istream 的operator bool:流正常为 true,读到 EOF / 出错为 false;

  6. 文件流四步:定义对象 → open(判断成功)→ 用>> <<read/write读写 → close;ofstream 默认清空文件,二进制要加ios::binary

  7. 二进制读写是内存原样拷贝,快但结构体里不能有指针、跨平台有隐患;文本读写人能看懂、好调试;

  8. stringstream底层就是一个自动扩容的 string,能做类型转换、字符串拼接、序列化;复用时记住clear()清状态 +str("")清内容两件套,只用一次就直接新建对象。

一句话收尾:流的本质,就是让你用操作键盘/屏幕的同一套方式(>><<),去操作文件和字符串——接口统一,是 C++ IO 流最大的设计思想

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询