1. IO流的基本概念
1)什么是流(Stream)?
流是一种数据传输的抽象概念,表示数据从一个地方流向另一个地方的过程。可以将流比作一条管道,数据就像水一样通过它流动。
- 输入流(Input Stream):数据从外部设备(如键盘、文件)流入到程序。
- 输出流(Output Stream):数据从程序流出到外部设备(如屏幕、文件)。
2)IO流的作用
简化数据的输入输出操作。抽象化底层设备操作,提供一致的编程接口。通过多态和继承实现灵活扩展,比如文件流、自定义流等。
3)IO流的分类
- 标准IO流 :负责与控制台交互的数据流,如
cin
,cout
,cerr
。 - 文件IO流 :用于处理文件的输入输出,如
ifstream
,ofstream
,fstream
。 - 字符串流 :处理内存中的字符串数据,如
istringstream
,ostringstream
,stringstream
(位于<sstream>
中)。
4)流的特点
- 类型安全:通过流操作符和函数模板,避免了直接操作设备带来的风险。
- 支持链式操作 :如
cout << "Hello" << " World" << endl;
。 - 可扩展性:通过继承和多态,可以自定义流类型以满足特殊需求。
- 状态管理 :流提供了一组状态标志(如
eof
,fail
),方便检测操作是否成功。
2. 标准输入
在使用C++标准输入流(std::cin
)时,需要注意以下几点:
*以下的代码中均包含<iostream>
的头文件,展开std的命名空间
a. 流的状态管理
std::cin
在读取数据时可能会遇到错误,这时需要检查和处理流的状态:
-
常见状态标志:
eof()
:是否到达输入结束标志(End of File)。fail()
:是否发生了格式化错误。更广泛的错误状态,包括"bad"错误和其他非严重错误。bad()
:是否发生了不可恢复的输入输出错误。good()
:流是否处于正常状态。
示例:
cppint main() { int num; cout << "Enter a number: "; cin >> num; if (cin.fail()) cout << "Invalid input! Please enter a valid number." << endl; else cout << "You entered: " << num << endl; return 0; }
b. 输入缓冲区的处理
-
std::cin
会从输入缓冲区读取数据。如果缓冲区中有多余的数据,可能会导致意外行为:- 如果输入了错误的类型(如输入字母而不是数字),缓冲区会保留错误数据,导致后续输入失败。
std::cin.ignore()
可以丢弃缓冲区中的多余数据。
示例:
cppint main() { int num; cout << "Enter a number: "; cin >> num; if (cin.fail()) { cin.clear(); // 清除错误状态 cin.ignore(100, '\n'); // 丢弃缓冲区中多余的数据 cout << "Invalid input. Try again." << endl; } else { cout << "You entered: " << num << endl; } return 0; }
-
如果输入数据的类型与预期不符,
std::cin
会进入失败状态,导致后续读取失败。 -
使用显式类型转换或验证输入类型可以避免这类问题。
示例:
cppint main() { int num; cout << "Enter a number: "; while (!(cin >> num)) { // 输入非数字时失败 cin.clear(); // 清除错误状态 cin.ignore(100, '\n'); // 丢弃缓冲区内容 cout << "Invalid input. Please enter a number: "; } cout << "You entered: " << num << endl; return 0; }
c. 读取整行输入
-
使用
std::cin
读取字符串时会遇到问题,因为它只读取空格前的部分,遇到空格就停止读入。如果需要读取整行,可以使用std::getline()
。 -
在混合使用
std::cin
和std::getline()
时,需要注意缓冲区中可能残留换行符。示例:
cpp#include <string> int main() { string name; int age; cout << "Enter your age: "; cin >> age; // 读取年龄 cin.ignore(); // 清除换行符 cout << "Enter your name: "; getline(cin, name); // 读取整行 cout << "Hello, " << name << ". You are " << age << " years old." << endl; return 0; }
d. 输入流同步问题
-
默认情况下,
std::cin
和 C 风格的输入(如scanf
)是同步的,可能会影响性能。 -
如果只使用 C++ 风格的流操作,可以通过
std::ios::sync_with_stdio(false)
关闭同步以提高速度。std::ios::sync_with_stdio(false)
被调用来关闭同步,然后程序分别使用C++的IO流和C语言的IO函数输出信息。由于关闭了同步,两个输出的顺序可能不确定。示例:
cppint main() { ios::sync_with_stdio(false); // 关闭同步 cin.tie(nullptr); // 解绑 `cin` 和 `cout` int num; cout << "Enter a number: "; cin >> num; cout << "You entered: " << num << endl; return 0; }
e. 特殊输入情况
- Ctrl+D(Linux/macOS)或 Ctrl+Z(Windows):表示输入结束(EOF)。
- 空输入:需要额外处理,例如在读取整行时判断空行。
3. 标准输出
在使用 C++ 标准输出流(std::cout
)时,有一些重要的注意点和技巧,可以帮助更高效地进行输出操作,同时避免潜在问题。
*以下的代码中均包含<iostream>
的头文件,展开std的命名空间。
a. 输出格式控制
-
使用 流操纵符(Manipulators)可以调整输出格式:
std::endl
:换行并刷新缓冲区。std::setw(n)
:设置字段宽度(需要引入<iomanip>
)。std::setprecision(n)
:设置浮点数精度。std::fixed
和std::scientific
:控制浮点数的输出格式。
示例:
cpp#include <iomanip> int main() { double num = 3.14159265358979; cout << "Default: " << num << endl; cout << "Fixed: " << fixed << setprecision(2) << num << endl; cout << "Scientific: " << scientific << num << endl; cout << setw(10) << 42 << endl; // 设置字段宽度 return 0; }
b. 缓冲区与刷新
-
自动刷新 :
std::endl
除了换行,还会刷新缓冲区(flush
操作)。- 刷新操作可能会降低性能,因此如果仅需换行,建议使用
\n
。
- 刷新操作可能会降低性能,因此如果仅需换行,建议使用
-
手动刷新 :可以用
std::cout.flush()
手动刷新缓冲区。示例:
cppint main() { cout << "Processing..." << flush; // 不换行,立刻输出 // 模拟延迟 for (int i = 0; i < 3; i++) { cout << "." << flush; } cout << "\nDone!" << endl; return 0; }
c. 标准错误输出
-
使用
std::cerr
进行错误消息输出:- 不带缓冲区,因此输出效率较高,适合实时错误提示。
-
使用
std::clog
进行日志记录:- 带缓冲区,适合输出非关键的日志信息。
示例:
cppint main() { cerr << "Error: Something went wrong!" << endl; clog << "Log: Program started." << endl; return 0; }
d. 国际化支持
-
C++ 支持基于本地化的输出,例如数字和货币格式。
-
使用
std::locale
可以改变流的本地化行为。示例:
cpp#include <locale> int main() { cout.imbue(locale("en_US.UTF-8")); // 使用美国英语本地化 cout << 1234567.89 << endl; return 0; }
常见问题总结
- 缓冲区刷新 :避免过度使用
std::endl
影响性能,尽量使用\n
或显式刷新。 - 混用
printf
和std::cout
:如无必要,建议避免混用,或关闭同步确保性能。 - 格式控制:注意浮点数精度与字段宽度设置,避免输出不符合预期。
通过正确管理 std::cout
,可以确保输出高效且美观,同时避免常见的输出问题。
以下是对 C++ 文件 IO 流 的详细介绍:
4. 文件IO流
a. 文件 IO 流的基本概念
-
文件流是 C++ 提供的用于对文件进行输入输出操作的机制,主要包含以下三种流类:
std::ifstream
:输入文件流,用于从文件中读取数据。std::ofstream
:输出文件流,用于向文件中写入数据。std::fstream
:文件流,可以同时支持文件的输入和输出操作。
-
文件流通过继承自标准流(如
std::istream
和std::ostream
)来复用流操作功能。
b. 文件流状态检查
文件流提供状态检查方法以确保文件操作的正确性:
is_open()
:检查文件是否成功打开。eof()
:是否到达文件末尾。fail()
:是否发生格式化错误。bad()
:是否发生严重错误。good()
:是否一切正常。
示例:
cpp
std::ifstream inputFile("example.txt");
if (!inputFile.is_open()) {
std::cerr << "Failed to open the file." << std::endl;
}
c. 文件操作的基本步骤
文件操作通常包括以下步骤:
c.1 打开文件
-
使用构造函数打开文件:
cppstd::ifstream inputFile("example.txt"); // 打开文件进行读取 std::ofstream outputFile("output.txt"); // 打开文件进行写入 std::fstream file("data.txt", std::ios::in | std::ios::out); // 读写模式
-
使用
open()
方法打开文件:cppstd::ifstream inputFile; inputFile.open("example.txt"); std::ofstream outputFile; outputFile.open("output.txt");
-
打开模式(
std::ios
标志):std::ios::in
:读模式(默认用于std::ifstream
)。std::ios::out
:写模式(默认用于std::ofstream
)。std::ios::app
:追加模式。std::ios::ate
:打开文件并将文件指针移到末尾。std::ios::binary
:二进制模式。std::ios::trunc
:如果文件存在,则清空文件内容。
c.2 读写文件
-
写文件(
std::ofstream
或std::fstream
):cppstd::ofstream outputFile("output.txt"); if (outputFile.is_open()) { outputFile << "Hello, World!" << std::endl; outputFile.close(); }
-
读文件(
std::ifstream
或std::fstream
):cppstd::ifstream inputFile("example.txt"); if (inputFile.is_open()) { std::string line; while (std::getline(inputFile, line)) { std::cout << line << std::endl; // 打印每一行 } inputFile.close(); }
c.3 关闭文件
-
文件操作完成后需要关闭文件以释放资源:
cppinputFile.close(); outputFile.close();
d. 二进制文件操作
-
对于二进制文件,使用
read()
和write()
方法。 -
示例:
cpp#include <fstream> using namespace std; struct Data { int id; char name[50]; }; int main() { Data d = {1, "Alice"}; // 写入二进制文件 ofstream outFile("data.bin", ios::binary); if (outFile.is_open()) { outFile.write(reinterpret_cast<char*>(&d), sizeof(d)); outFile.close(); } // 从二进制文件读取 Data readData; ifstream inFile("data.bin", ios::binary); if (inFile.is_open()) { inFile.read(reinterpret_cast<char*>(&readData), sizeof(readData)); inFile.close(); } cout << "ID: " << readData.id << ", Name: " << readData.name << endl; return 0; }
e. 文件流中的高级功能
e.1 文件指针的操作
-
文件流支持文件指针的移动:
seekg(offset, dir)
:移动输入指针。seekp(offset, dir)
:移动输出指针。tellg()
:获取输入指针的位置。tellp()
:获取输出指针的位置。
示例:
cppfile.seekg(0, std::ios::end); // 移动到文件末尾 auto fileSize = file.tellg(); // 获取文件大小 file.seekg(0, std::ios::beg); // 移回文件开头
e.2 自定义缓冲区
- 文件流允许使用自定义缓冲区来优化性能(较为高级)。
f. 常见问题与注意事项
- 未能打开文件:确保文件路径正确,必要时添加错误检查。
- 文件操作权限:在某些操作系统上需要检查文件的读写权限。
- 覆盖文件内容 :
std::ofstream
默认会覆盖文件内容,如需追加,使用std::ios::app
模式。 - 二进制与文本模式的区别:文本模式可能会对换行符进行转换,而二进制模式不会。
g. 综合示例
以下是一个综合的文件读写示例:
cpp
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
// 写文件
ofstream outFile("example.txt");
if (outFile.is_open()) {
outFile << "Hello, World!" << endl;
outFile << "This is a test file." << endl;
outFile.close();
} else {
cerr << "Failed to open file for writing." << endl;
}
// 读文件
ifstream inFile("example.txt");
if (inFile.is_open()) {
string line;
while (getline(inFile, line)) {
cout << line << endl; // 输出每一行
}
inFile.close();
} else {
cerr << "Failed to open file for reading." << endl;
}
return 0;
}
【总结】 文件 IO 流是 C++ IO 系统的重要组成部分,提供了对文件的高效操作支持。在实际应用中,合理利用文件流可以极大提高程序的功能和鲁棒性。
如果记不住的话,可以在需要用的时候再回来查,记得收藏一波不迷路哦~