文章目录
前言
简单分享一下c++ IO相关的一些知识点,希望对大家有用
c++ IO 类简介
C++的IO类是标准库的一部分,主要用于输入和输出操作。这些类提供了读写数据的接口,无论是从标准输入输出(如键盘和屏幕)还是从文件。以下是一些核心的C++ IO类及其主要功能:
1. iostream库
iostream
库包含用于标准输入输出操作的基本类。
iostream
类
istream
:用于输入操作的基类,例如从键盘读取数据。ostream
:用于输出操作的基类,如向屏幕输出数据。iostream
:istream
和ostream
的组合,用于同时支持输入和输出。
标准IO对象
std::cin
:标准输入流对象,与istream
类关联,通常用于从键盘读取输入。std::cout
:标准输出流对象,与ostream
类关联,用于向屏幕输出数据。std::cerr
:标准错误流对象,也与ostream
类关联,用于输出错误消息。默认情况下,cerr
是非缓冲的,即立即输出。std::clog
:类似于cerr
,但通常用于记录日志。clog
是缓冲的,所以输出可能不会立即出现。
2. fstream库
fstream
库提供了文件读写功能。
fstream
类
ifstream
:用于从文件读取数据的输入流类。继承自istream
。ofstream
:用于向文件写入数据的输出流类。继承自ostream
。fstream
:结合了ifstream
和ofstream
的功能,支持同时读写文件。继承自iostream
。
3. stringstream库
sstream
库提供了字符串流功能,即使用字符串进行输入输出操作。
stringstream
类
istringstream
:类似于istream
,但用于从字符串读取数据。ostringstream
:类似于ostream
,但用于向字符串写入数据。stringstream
:结合了istringstream
和ostringstream
的功能,支持同时对字符串进行读写。
格式化和控制
C++ IO类还提供了丰富的格式化和控制功能,例如设置小数点后的位数、设置宽度、填充字符等,这些都是通过流操纵符(如 std::setprecision
、std::setw
等)实现的。
错误处理
流对象内部维护着状态标志,可以用来检测操作是否成功。例如:
eof()
:检查是否达到文件末尾(End Of File)。fail()
:检查是否发生了逻辑错误,如格式不匹配。bad()
:检查是否发生了严重错误,如读写操作失败。
IO对象无拷贝或赋值
在C++中,IO对象(如 std::cin
, std::cout
, std::ifstream
, std::ofstream
等)是不可拷贝和不可赋值的。这意味着不能使用拷贝构造函数或赋值操作符来复制或赋值这些对象。这样的设计是出于几个原因:
-
唯一性:每个IO对象都对应一个唯一的资源,例如标准输入、标准输出或特定的文件。允许复制这些对象可能导致对同一资源的多个访问点,这可能引发混乱和不一致性。
-
资源管理:IO对象通常与底层资源(如文件句柄)紧密绑定。拷贝IO对象可能导致资源管理问题,例如资源泄露或重复关闭文件。
-
状态共享:如果IO对象可以被拷贝,那么它们的状态(如错误状态、文件指针位置等)也将被共享。这种状态共享可能导致难以追踪的错误和不可预测的行为。
因此,为了保证资源的正确管理和使用,C++标准库设计了IO对象为不可拷贝和不可赋值。如果需要传递这些对象,通常的做法是使用引用或指针。例如,可以将 std::ostream
对象作为引用参数传递给一个函数,从而允许该函数向任何输出流(如 std::cout
或文件输出流)写入数据。
示例:
cpp
void writeToStream(std::ostream& stream) {
stream << "Hello, World!";
}
int main() {
writeToStream(std::cout); // 使用标准输出流
std::ofstream file("example.txt");
writeToStream(file); // 使用文件输出流
// ...
}
IO条件状态
C++标准库中的IO流(如 std::istream
、std::ostream
、以及从它们派生出的类型,比如 std::ifstream
、std::ofstream
)维护了一个内部状态,该状态表示流的当前健康状况。这个状态是通过一组状态标志来表示的,可以用来检查在执行IO操作过程中是否发生了错误。
主要的状态标志
-
eofbit:表示已经到达输入流的末尾(End-Of-File)。例如,如果你试图从文件读取数据,但已经到达文件末尾,那么这个标志会被设置。
-
failbit:表示一个IO操作失败,但不是由于到达文件末尾或发生严重错误造成的。例如,当试图读取一个整数,但输入的是一个字母时,这个标志会被设置。
-
badbit:表示发生了严重错误(如无法读取数据)。这通常意味着流不能继续使用。
-
goodbit:表示流处于良好状态,没有错误发生。其值通常为0。
检查流状态
流对象提供了一些成员函数来检查这些状态标志:
good()
:如果没有任何错误标志被设置(即流处于良好状态),则返回true
。eof()
:如果设置了eofbit
,则返回true
。fail()
:如果设置了failbit
或badbit
,则返回true
。bad()
:如果设置了badbit
,则返回true
。
控制流状态
clear()
:用于重置流的状态标志。你可以传递一个特定的标志位作为参数,或者不传递参数来清除所有的错误标志。setstate()
:用于设置特定的状态标志。
示例
cpp
#include <iostream>
#include <fstream>
int main() {
std::ifstream file("example.txt");
if (!file) {
if (file.eof()) {
std::cout << "Reached end of file." << std::endl;
}
if (file.fail()) {
std::cout << "IO operation failed." << std::endl;
}
if (file.bad()) {
std::cout << "Critical IO error." << std::endl;
}
} else {
// 文件读取或处理操作
}
file.close();
return 0;
}
在这个示例中,打开一个文件进行读取,并检查文件流对象 file
是否处于良好状态。如果有错误发生,就检查是哪种类型的错误并做出相应的反应。
管理输出缓冲
主要操作
-
刷新缓冲区:将缓冲区的内容立即输出到目标设备。
- 使用
std::flush
:它会刷新关联的缓冲区但不输出任何额外内容。 - 使用
std::endl
:它会插入换行符并刷新缓冲区。
- 使用
-
同步与不同步I/O:
- 使用
std::ios::sync_with_stdio(false)
:禁用C++流和C标准I/O之间的同步,可以提高程序性能,但在混用C和C++风格的I/O时需小心。
- 使用
-
设置缓冲区大小:
- 使用
rdbuf()->pubsetbuf(char* buffer, std::streamsize buffer_size)
来自定义流的缓冲区大小和位置。
- 使用
示例
cpp
#include <iostream>
#include <fstream>
int main() {
// 禁用同步
std::ios::sync_with_stdio(false);
// 使用std::flush刷新缓冲区
std::cout << "Hello, World!" << std::flush;
// 使用std::endl插入换行并刷新缓冲区
std::cout << "Another line" << std::endl;
// 使用自定义缓冲区进行文件操作
char buffer[1024];
std::ofstream file("example.txt");
file.rdbuf()->pubsetbuf(buffer, sizeof(buffer));
// ... 文件写入操作 ...
// 刷新并关闭文件
file << std::flush;
file.close();
return 0;
}
在这个示例中,使用 std::flush
和 std::endl
来管理标准输出流的缓冲区。
文件输入输出
在C++中,文件输入输出是通过文件流对象实现的,这些对象包含在标准库 <fstream>
中。主要包括三种类型的文件流对象:std::ifstream
用于文件读取,std::ofstream
用于文件写入,以及 std::fstream
既可以用于读取也可以用于写入。
使用文件流对象
-
std::ifstream
(输入文件流):- 用于从文件中读取数据。
- 你可以使用成员函数
open()
打开文件,或在创建对象时指定文件名。 - 使用
>>
运算符或getline()
函数从文件中读取数据。
-
std::ofstream
(输出文件流):- 用于向文件写入数据。
- 同样,可以使用
open()
方法或在构造函数中指定文件名来打开文件。 - 使用
<<
运算符向文件中写入数据。
-
std::fstream
(文件流):- 结合了
ifstream
和ofstream
的功能,可以同时用于读取和写入。 - 打开文件时,需要指定模式(如读、写或两者)。
- 结合了
示例
cpp
#include <fstream>
#include <iostream>
#include <string>
int main() {
// 写入文件
std::ofstream outFile("test.txt");
outFile << "Hello, file!" << std::endl;
outFile.close();
// 读取文件
std::ifstream inFile("test.txt");
std::string line;
while (getline(inFile, line)) {
std::cout << line << std::endl;
}
inFile.close();
}
文件模式
文件模式是指打开文件时指定的模式,它决定了如何操作文件。常见的文件模式包括:
- 输入(
std::ios::in
):文件以读取模式打开。 - 输出(
std::ios::out
) :文件以写入模式打开。如果文件已存在,则其内容会被覆盖,除非同时使用了std::ios::app
。 - 二进制(
std::ios::binary
):以二进制模式打开文件。 - 追加(
std::ios::app
):所有输出操作都在文件末尾进行,不覆盖文件原有内容。 - 同时读写(
std::ios::in | std::ios::out
):文件同时用于读取和写入。
示例:使用不同文件模式
cpp
#include <fstream>
int main() {
// 以写入模式打开文件,如果文件存在,则覆盖内容
std::ofstream outFile("test.txt", std::ios::out);
// 以追加模式打开文件,写入的内容添加到文件末尾
std::ofstream appendFile("test.txt", std::ios::app);
// 以二进制和读写模式打开文件
std::fstream binaryFile("test.bin", std::ios::in | std::ios::out | std::ios::binary);
}
string流
在C++中,字符串流(String Stream)提供了一种方便的方式来处理字符串数据。这些功能通过 <sstream>
头文件中的类实现,包括 std::stringstream
、std::istringstream
和 std::ostringstream
。
1. std::stringstream
std::stringstream
类结合了输入和输出功能,可以用于同时读取和写入字符串。它通常用于格式化字符串、从字符串解析数据,或者将多种数据类型组合成一个字符串。
基本用法
- 将数据写入字符串流。
- 从字符串流中读取数据。
- 作为动态字符串的构建器。
示例
cpp
#include <sstream>
#include <iostream>
int main() {
std::stringstream ss;
ss << "Example " << 123 << " "; // 写入字符串和整数
ss << 45.67; // 继续写入浮点数
std::string str = ss.str(); // 获取整个字符串
std::cout << str << std::endl; // 输出: "Example 123 45.67"
int num;
ss >> str >> num; // 从流中读取数据
std::cout << str << ", " << num << std::endl; // 输出: "Example, 123"
}
2. std::istringstream
std::istringstream
类专门用于从字符串读取数据,类似于 std::ifstream
,但操作的是字符串而非文件。
基本用法
- 从字符串解析数据。
- 类似于使用
>>
运算符从std::cin
读取数据。
示例
cpp
#include <sstream>
#include <iostream>
int main() {
std::string data = "42 Hello";
std::istringstream iss(data);
int num;
std::string word;
iss >> num >> word;
std::cout << "Number: " << num << ", Word: " << word << std::endl; // 输出: "Number: 42, Word: Hello"
}
3. std::ostringstream
std::ostringstream
类专门用于向字符串写入数据,类似于 std::ofstream
,但操作的是字符串而非文件。
基本用法
- 将多种数据格式化为字符串。
- 类似于使用
<<
运算符向std::cout
写入数据。
示例
cpp
#include <sstream>
#include <iostream>
int main() {
std::ostringstream oss;
oss << "Number: " << 42 << ", Word: " << "Hello";
std::string str = oss.str(); // 获取构建的字符串
std::cout << str << std::endl; // 输出: "Number: 42, Word: Hello"
}
使用场景
- 数据的串行化和反串行化。
- 从复杂字符串中解析不同类型的数据。
- 构建复杂的格式化字符串。
- 用于单元测试中以字符串形式比较复杂对象的输出。
总结
IO这块还有很多可以深入研究的地方,同步/异步,阻塞/非阻塞等,今天简单介绍一下,后续补充详细的知识。