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