C流是什么意思?
摘要:在 C++ 标准库中,stream(流)是处理输入输出(IO)的核心抽象,分为输入流(istream)、输出流(ostream)、输入输出流(iostream)等多种类型,且支持面向不同设备(文件、字符
在 C++ 标准库中,stream(流)是处理输入输出(I/O)的核心抽象,分为输入流(istream)、输出流(ostream)、输入输出流(iostream)等多种类型,且支持面向不同设备(文件、字符串、控制台等)的具体流类型。
Stream 类型
类别
主要类型及说明
头文件
输入流
std::istream:通用输入流基类
<istream>
std::ifstream:文件输入流
<fstream>
std::istringstream:字符串输入流
<sstream>
输出流
std::ostream:通用输出流基类
<ostream>
std::ofstream:文件输出流
<fstream>
std::ostringstream:字符串输出流
<sstream>
输入输出流
std::iostream:通用输入输出流基类
<iostream>
std::fstream:文件输入输出流
<fstream>
std::stringstream:字符串输入输出流
<sstream>
标准流对象
std::cin:标准输入流(全局对象)
<iostream>
std::cout:标准输出流(全局对象)
<iostream>
std::cerr:标准错误输出流(无缓冲)
<iostream>
std::clog:标准错误输出流(带缓冲)
<iostream>
继承关系简图(简化版)
std::ios_base
└── std::ios
├── std::istream
│ ├── std::ifstream
│ └── std::istringstream
├── std::ostream
│ ├── std::ofstream
│ └── std::ostringstream
└── std::iostream
├── std::fstream
└── std::stringstream
一、标准流
C++ 标准库定义了四个主要的标准流对象:
对象名
类型
功能描述
缓冲机制
用途
std::cin
std::istream
标准输入流,通常关联键盘输入
带缓冲
从键盘或重定向输入读取数据
std::cout
std::ostream
标准输出流,通常关联控制台显示
带缓冲
向控制台或重定向输出写数据
std::cerr
std::ostream
标准错误输出流,关联控制台显示
无缓冲
用于错误或即时输出提示
std::clog
std::ostream
标准日志输出流,关联控制台显示
带缓冲
用于程序日志信息输出
1. std::cin - 标准输入流
类型:std::istream
默认关联设备:键盘(终端输入)
用法:
int x;
std::cin >> x; // 从标准输入读取一个整数
缓冲:带缓冲。输入时会先缓冲一部分数据,通常是整行读取后交给程序处理。
支持格式化输入:operator>> 会自动跳过空白、换行,进行类型转换。
支持重定向:例如从文件读取时,命令行重定向输入 program < input.txt。
2. std::cout - 标准输出流
类型:std::ostream
默认关联设备:控制台(终端显示)
用法:
std::cout << "Hello, world!" << std::endl;
缓冲:带缓冲。缓冲区满或者遇到 std::endl 会刷新缓冲区写到终端。
支持各种类型的格式化输出(整型、浮点、字符串、自定义类型重载 operator<<)。
支持重定向:例如把输出写入文件 program > output.txt。
3. std::cerr - 标准错误输出流
类型:std::ostream
默认关联设备:控制台(终端显示)
无缓冲输出:写入时立即发送到终端,适合打印错误或警告信息,确保及时可见。
用法:
std::cerr << "Error: File not found!" << std::endl;
作用是和 cout 分开输出,方便终端或脚本捕获错误信息。
不受缓冲影响,即使程序异常退出也更容易保证错误信息打印出来。
4. std::clog - 标准日志输出流
类型:std::ostream
默认关联设备:控制台(终端显示)
带缓冲输出,适合大量日志信息,效率高。
用法:
std::clog << "Log: Starting the program..." << std::endl;
用于日志信息输出,不同于 cerr 主要是“错误”,clog 主要是“日志”。
和 cerr 区别在于缓冲方式和输出语义。
标准流对象常用操作和特性
操作
示例
说明
读取输入
std::cin >> x;
从键盘读取格式化输入
输出文本
std::cout << "Hello\n";
向终端打印文本
输出错误信息
std::cerr << "Error!\n";
立即打印错误信息
输出日志
std::clog << "Log message\n";
缓冲写入日志信息
刷新缓冲区
std::cout.flush(); 或 std::cout << std::flush;
立即将缓冲内容写入设备
操纵符换行+刷新
std::cout << std::endl;
输出换行并刷新缓冲
判断流状态
if (!std::cin) { /* 读取失败 */ }
判断流是否处于错误状态
重定向流
命令行重定向 program < input.txt > output.txt
改变流的输入输出设备
标准流对象的缓冲策略
流对象
缓冲类型
说明
std::cin
带缓冲
读取时先缓冲,优化输入效率
std::cout
带缓冲
输出时缓存,遇换行或缓冲满时刷新
std::cerr
无缓冲
错误信息立即输出,不等待缓冲刷新
std::clog
带缓冲
日志输出缓冲,提高效率
二、输入输出流
在C++中,输入输出流(Input/Output Stream)是对数据流动方向的抽象,用于从某个设备(如键盘、文件、内存)读取数据或向其写入数据。输入流负责读取,输出流负责写入。输入输出流即同时支持输入和输出的流,能既从中读取数据,也向其中写入数据。
基本流层级和具体实现类
std::iostream
继承自 std::istream 和 std::ostream。支持标准输入输出流的所有操作。既可以用作输入流,也可以用作输出流。
底层依赖于缓冲区(buffer)管理,避免频繁系统调用,提升效率。iostream 对象拥有一个streambuf缓冲区指针,通过它实现具体读写。iostream 继承了格式化输入输出的能力,比如支持operator>>、operator<<对各种类型的重载。
类名
功能描述
头文件
std::ios_base
I/O 库的基础类,管理格式化和状态
<ios>
std::ios
继承自 ios_base,增加缓冲等支持
<ios>
std::istream
输入流基类
<istream>
std::ostream
输出流基类
<ostream>
std::iostream
输入输出流基类(继承自 istream 和 ostream)
<iostream>
iostream继承了输入流和输出流的功能,因此可同时读写。
具体实现类:
类名
用途
头文件
std::fstream
文件输入输出流
<fstream>
std::stringstream
内存字符串输入输出流
<sstream>
1. 文件读写(使用 std::fstream)
#include <fstream>
#include <iostream>
#include <string>
int main() {
std::fstream file("example.txt", std::ios::in | std::ios::out | std::ios::trunc);
if (!file) {
std::cerr << "文件打开失败\n";
return 1;
}
// 写入数据
file << "Hello, iostream!\n";
// 重置文件读取位置到开头
file.seekg(0);
// 读取数据
std::string line;
std::getline(file, line);
std::cout << "文件内容: " << line << std::endl;
file.close();
return 0;
}
2. 内存字符串读写(使用 std::stringstream)
#include <sstream>
#include <iostream>
int main() {
std::stringstream ss;
// 写入
ss << "123 456";
int a, b;
// 读取
ss >> a >> b;
std::cout << "读取到的数字:" << a << ", " << b << std::endl;
}
//输出: 读取到的数字:123, 456
常用成员函数(部分)
函数
说明
operator>>
格式化输入(读取)
operator<<
格式化输出(写入)
read(char*, size_t)
读取原始字节流
write(char*, size_t)
写入原始字节流
get()
读取一个字符
put(char)
写入一个字符
seekg(pos)
设置输入位置
seekp(pos)
设置输出位置
tellg()
获取当前输入位置
tellp()
获取当前输出位置
good(), fail(), eof()
检查流状态
flush()
刷新缓冲区
流状态及错误处理
在 C++ 中,输入输出流(iostream)提供了状态标志和错误处理机制,用于检测和处理 I/O 操作中的异常或错误情况。理解这些机制对于编写健壮的 I/O 代码非常重要。
流的状态标志(Stream State Flags)
C++ 的流对象(如 std::ifstream, std::ofstream, std::cin, std::cout 等)都继承自 std::ios_base 类,并维护一组状态标志来表示流当前的状态。这些状态标志是通过成员函数 rdstate() 获取的,其类型为 std::ios_base::iostate。
常见的状态标志包括:
标志
含义
goodbit
没有发生错误(一切正常)
badbit
发生了不可恢复的读写错误(例如底层设备出错)
failbit
输入/输出操作失败(但流仍可用,例如类型不匹配)
eofbit
到达文件末尾(EOF)
示例:
#include <iostream>
#include <fstream>
int main() {
std::ifstream file("example.txt");
if (!file) {
std::cerr << "Failed to open file.\n";
}
int value;
file >> value;
if (file.fail()) {
std::cerr << "Input failed: type mismatch or bad input.\n";
}
if (file.bad()) {
std::cerr << "Serious I/O error occurred.\n";
}
if (file.eof()) {
std::cerr << "End of file reached.\n";
}
if (file.good()) {
std::cout << "Everything is fine.\n";
}
return 0;
}
状态检查函数
除了直接使用 rdstate() 来获取状态位外,还可以使用以下成员函数进行状态判断:
函数名
返回值说明
good()
是否处于良好状态(没有设置任何错误标志)
bad()
是否发生了不可恢复的错误
fail()
是否发生了可恢复的错误(包括 eofbit)
eof()
是否到达文件末尾
operator!()
是否处于失败状态(即 fail() 为真)
operator bool()
是否处于非失败状态(即 !fail() 为真)
清除状态标志
如果你希望继续使用一个已经出错的流对象,可以使用 clear() 或 clear(iostate) 函数来清除状态标志。
示例:
#include <iostream>
int main() {
int num;
std::cin >> num;
if (std::cin.fail()) {
std::cin.clear(); // 清除 failbit
std::cin.ignore(10000, '\n'); // 忽略缓冲区中的非法输入
std::cout << "Invalid input. Please enter an integer: ";
std::cin >> num;
}
std::cout << "You entered: " << num << std::endl;
return 0;
}
错误处理与异常机制(Exception Handling)
默认情况下,C++ 流不会抛出异常,而是通过状态标志来报告错误。但你可以通过 exceptions() 函数启用异常处理。
示例:启用异常
#include <iostream>
#include <fstream>
int main() {
std::ifstream file("nonexistent.txt");
// 启用异常:当流状态变为 badbit 或 failbit 时抛出异常
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
int value;
file >> value; // 如果文件不存在或无法读取,将抛出异常
} catch (const std::ios_base::failure& e) {
std::cerr << "Caught an exception: " << e.what() << '\n';
}
return 0;
}
⚠️ 注意:启用异常后,某些操作(如构造失败)可能不会触发异常。建议结合 if (!file) 进行检查。
三、输入流
在 C++ 中,输入流(Input Stream) 是一种用于从数据源读取数据的机制。C++ 的标准库提供了强大的 I/O 流类库,其中与输入相关的类主要包括:
std::istream:基本输入流类。
std::ifstream:用于从文件中读取数据。
std::istringstream:用于从字符串中读取数据。
std::cin:标准输入流对象(通常来自键盘)。
所有输入流都继承自 std::istream 类。它的核心功能是提供读取操作符 >> 和成员函数如 get(), getline(), read() 等。
常用输入流对象:
对象/类
描述
std::cin
标准输入流,默认从控制台读取
std::ifstream
文件输入流
std::istringstream
字符串输入流
常用输入方法
1. 使用 operator >>(提取运算符)
这是最常用的输入方式,用于按类型读取数据。
#include <iostream>
int main() {
int age;
std::cout << "Enter your age: ";
std::cin >> age;
std::cout << "You are " << age << " years old.\n";
return 0;
}
注意:
它会自动跳过前导空白字符(空格、换行、制表符等)。
如果输入类型不匹配(比如输入字母而非数字),将设置 failbit 标志。
2. 使用 get() 函数
用于逐个或批量读取字符(包括空白字符)。
单字符读取:
#include <iostream>
int main() {
char ch;
while (std::cin.get(ch)) {
std::cout << ch;
}
return 0;
}
多字符读取(带缓冲区):
#include <iostream>
int main() {
char buffer[100];
std::cin.get(buffer, 100);
std::cout << "You entered: " << buffer << std::endl;
return 0;
}
get() 不会跳过空白字符,并且不会自动添加 \0 结尾。
3. 使用 getline() 函数
用于读取整行输入,直到遇到换行符。
#include <iostream>
int main() {
char buffer[100];
std::cin.get(buffer, 100);
std::cout << "You entered: " << buffer << std::endl;
return 0;
}
或者使用 std::string 版本:
#include <iostream>
#include <string>
int main() {
std::string line;
std::getline(std::cin, line);
std::cout << "You entered: " << line << std::endl;
return 0;
}
getline() 更适合读取包含空格的完整句子。
4. 使用 read() 函数(二进制模式)
用于一次性读取指定数量的字节,常用于二进制文件处理。
char buffer[100];
std::cin.read(buffer, sizeof(buffer));
std::streamsize count = std::cin.gcount();
std::cout.write(buffer, count); // 输出已读取的内容
文件输入流ifstream
std::ifstream(input file stream)是文件输入流,用于从文件中读取数据,就像 std::cin 用于从标准输入读取一样。
它是 C++ 的 IO 库中的一部分,定义在头文件 <fstream> 中。
#include <fstream>
继承关系如下:
std::ios_base
↑
std::ios
↑
std::istream
↑
std::ifstream
常用构造函数
构造函数形式
说明
ifstream()
创建未打开任何文件的流对象
ifstream(const char* filename)
打开一个文件用于读取,使用默认模式(文本模式)
ifstream(const char* filename, ios::openmode mode)
指定模式打开文件
ifstream(const std::string& filename, ios::openmode mode)
使用 std::string 作为文件名
常见打开模式(std::ios::openmode)
你可以传给 ifstream 构造函数或者 open() 方法的模式:
模式
含义
ios::in
以读模式打开(默认)
ios::binary
以二进制模式打开
ios::ate
打开文件并定位到文件尾
ios::app
追加模式(ifstream 很少用)
ios::trunc
清空文件(仅用于 ofstream)
⚠️ 注意:ifstream 默认自带 ios::in,所以你不用显式加 unless 和其他模式组合。
常见用法示例
🔸 1. 基本读取一整行
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream infile("example.txt");
if (!infile.is_open()) {
std::cerr << "Failed to open file.\n";
return 1;
}
std::string line;
while (std::getline(infile, line)) {
std::cout << "Line: " << line << '\n';
}
infile.close();
return 0;
}
🔸 2. 按单词读取
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream infile("example.txt");
if (!infile.is_open()) {
std::cerr << "Failed to open file.\n";
}
std::string word;
while (infile >> word) {
std::cout << "Word: " << word << '\n';
}
infile.close();
return 0;
}
🔸 3. 按数字读取
#include <iostream>
#include <fstream>
#include <string>
int main() {
try {
std::ifstream infile("/home/fxq/untitled3/example.txt");
if (!infile.is_open()) {
std::cerr << "Failed to open file.\n";
}
int num;
while (infile >> num) {
std::cout << "Read number: " << num << '\n';
}
infile.close();
} catch (const std::runtime_error& e) {
std::cerr << "发生异常: " << e.what() << std::endl;
}
return 0;
}
🔸 4. 用 .open() 延迟打开
std::ifstream infile;
infile.open("data.txt");
if (!infile) {
std::cerr << "Failed to open.\n";
}
缓冲区
C++ 的 std::ifstream 默认是带缓冲的,其底层使用了一个缓冲区(buffer)*来优化文件读取效率。但你可以*自定义缓冲区大小和内存,甚至设置成无缓冲。下面是详细讲解。
默认情况下:ifstream 自带缓冲
ifstream 会使用系统默认的缓冲区(通常是 4KB 或 8KB)。
当你调用 read()、getline()、operator>> 等函数时,数据先进入缓冲区,再从中解析或返回给用户程序。
好处是:减少系统调用次数,提高读取性能。
自定义缓冲区:rdbuf()->pubsetbuf()
你可以设置自己的缓冲区或取消缓冲区:
std::ifstream file("example.txt");
char buffer[4096]; // 自定义缓冲区
file.rdbuf()->pubsetbuf(buffer, sizeof(buffer));
pubsetbuf(buffer, size):设置缓冲区起始地址和大小
只能在 open() 之前调用(对已打开文件无效)
设置无缓冲模式(强烈不推荐除非特殊需求)
file.rdbuf()->pubsetbuf(nullptr, 0); // 禁用缓冲
⚠️ 这会让每一次 read() 或 getline() 都变成系统调用,性能会大幅下降,只在需要极低延迟的数据交互中才使用(如串口通信等)。
完整示例:自定义缓冲区读取大文件
#include <iostream>
#include <fstream>
int main() {
// 有问题试一下全路径
std::ifstream file("example.txt");
// 自定义缓冲区
char custom_buffer[8192]; // 8KB
file.rdbuf()->pubsetbuf(custom_buffer, sizeof(custom_buffer));
if (!file) {
std::cerr << "打开文件失败\n";
return 1;
}
std::string line;
size_t count = 0;
while (std::getline(file, line)) {
std::cout << line << std::endl;
++count;
}
std::cout << "共读取行数:" << count << "\n";
file.close();
return 0;
}
推荐场景
场景
缓冲设置建议
大文件(几百 MB 以上)
自定义大缓冲区(如 64KB)
小文件 / 一次性读取
默认缓冲即可
实时读取(如串口)
pubsetbuf(nullptr, 0)
多线程读取(少见)
每线程独立缓冲区
字符串输入流(istringstream)
用于从字符串中解析数据,常用于字符串拆分和格式转换。
#include <sstream>
#include <iostream>
int main() {
std::string data = "John 25 85.5";
std::istringstream iss(data);
std::string name;
int age;
double score;
iss >> name >> age >> score;
std::cout << name << ", " << age << ", " << score << std::endl;
return 0;
}
总结
方法
用途
>>
提取数据,自动跳过空白,类型安全
get()
读取字符(含空白)
getline()
读取一行(推荐用于整行输入)
read()
读取二进制数据
gcount()
获取最后一次读取的字符数
clear()
清除错误状态
ignore()
忽略缓冲区中的无效字符
四、输出流
输出流是 C++ I/O 库中的一个核心概念,用于向外部目标输出数据,目标可以是:
控制台(标准输出 std::cout)
文件(std::ofstream)
字符串(std::ostringstream)
自定义设备(如网络、GUI 等)
std::ios_base
└── std::ios
└── std::ostream // 输出流基类
├── std::ofstream // 文件输出流
├── std::ostringstream // 字符串输出流
└── std::iostream // 同时支持输入输出
常见输出流类型
类型
功能
头文件
std::ostream
所有输出流的基类
<ostream>
std::cout
标准输出流(控制台)
<iostream>
std::cerr
错误输出流(无缓冲)
<iostream>
std::clog
日志输出流(有缓冲)
<iostream>
std::ofstream
文件输出流
<fstream>
std::ostringstream
字符串输出流
<sstream>
常见输出函数
函数名
功能说明
示例
operator<<
格式化输出
os << x
put(char)
输出一个字符
os.put('A')
write(char*, size_t)
输出字节流(通常用于二进制)
os.write(buf, n)
flush()
刷新缓冲区,强制输出
os.flush()
格式化控制(使用 std::iomanip)
#include <iostream>
#include <iomanip>
int main() {
double pi = 3.14159265;
std::cout << std::fixed << std::setprecision(2) << pi << std::endl; // 输出 3.14
}
控制符
功能
std::setw(n)
设置字段宽度
std::setfill(c)
设置填充字符
std::setprecision(n)
设置浮点数精度
std::fixed
固定小数位格式
std::scientific
科学计数法格式
std::hex/std::dec/std::oct
设置整数进制
std::left / std::right / std::internal
对齐方式
文件输出流(std::ofstream)示例
#include <fstream>
int main() {
std::ofstream ofs("output.txt");
if (!ofs) {
std::cerr << "无法打开文件!" << std::endl;
return 1;
}
ofs << "写入文件的第一行" << std::endl;
ofs.close();
}
字符串输出流(std::ostringstream)
#include <sstream>
#include <iostream>
int main() {
std::ostringstream oss;
oss << "Name: " << "Alice" << ", Age: " << 30;
std::string result = oss.str(); // 获取写入的字符串
std::cout << "字符串流结果: " << result << std::endl;
}
输出流的缓冲刷新机制
会自动刷新缓冲区的情况:
std::endl:输出换行并刷新
flush() 手动刷新
缓冲区满时自动刷新
程序正常退出时自动刷新
std::cerr 是无缓冲的
五、重定向流
C++ 中流的重定向(redirect)是指改变标准输入输出流(如 std::cout、std::cin)的目标,使它们指向其他设备(如文件、字符串、网络等),而不是默认的控制台(终端)。这对于日志保存、测试、捕获输出都非常有用。
重定向 std::cout、std::cerr 到文件
方法:修改流缓冲区(streambuf)
std::cout 等流对象内部都有一个缓冲区指针 rdbuf(),通过替换它,我们可以让输出写到别处。
代码示例:把 std::cout 重定向到文件
#include <iostream>
#include <fstream>
int main() {
std::ofstream file("example.txt");
if (!file) {
std::cerr << "打开文件失败!" << std::endl;
return 1;
}
// 备份原 cout 缓冲区指针
std::streambuf* old_cout_buf = std::cout.rdbuf();
// 将 cout 缓冲区指向文件流的缓冲区
std::cout.rdbuf(file.rdbuf());
// 现在所有 cout 输出写入文件
std::cout << "这行写入了 example.txt 文件" << std::endl;
// 恢复原来的缓冲区(输出回终端)
std::cout.rdbuf(old_cout_buf);
std::cout << "这行又输出到终端了" << std::endl;
return 0;
}
重定向 std::cin 从文件读取
#include <iostream>
#include <fstream>
int main() {
std::ifstream file("/home/fxq/untitled3/example.txt");
if (!file) {
std::cerr << "打开文件失败!" << std::endl;
return 1;
}
// 备份原 cin 缓冲区指针
std::streambuf* old_cin_buf = std::cin.rdbuf();
// 将 cin 缓冲区指向文件流
std::cin.rdbuf(file.rdbuf());
std::string line;
std::getline(std::cin, line);
std::cout << "从文件读取的内容: " << line << std::endl;
// 恢复原缓冲区
std::cin.rdbuf(old_cin_buf);
return 0;
}
重定向流到字符串(内存)
可以用 std::stringstream:
#include <iostream>
#include <sstream>
int main() {
std::stringstream ss;
// 重定向 cout 到 stringstream
std::streambuf* old_buf = std::cout.rdbuf(ss.rdbuf());
std::cout << "Hello, 内存流!" << std::endl;
// 恢复 cout
std::cout.rdbuf(old_buf);
// 获取写入的字符串
std::string captured = ss.str();
std::cout << "捕获的字符串内容是: " << captured << std::endl;
return 0;
}
总结重定向关键点
操作步骤
说明
保存旧缓冲区指针
std::streambuf* old_buf = stream.rdbuf();
替换缓冲区指针
stream.rdbuf(new_stream.rdbuf());
使用新的流进行输入输出
输出会重定向到新流
恢复原缓冲区指针
stream.rdbuf(old_buf);
补充:命令行重定向
操作系统层面也支持重定向,不用代码实现,例如:
./program > output.txt # 重定向 stdout
./program < input.txt # 重定向 stdin
./program 2> error.log # 重定向 stderr
