IO类
一、IO类
c++不直接处理输入和输出,而是通过标准库中的类型处理IO。C++ 的 I/O(输入/输出)库主要包含在 < ifstreamtream >、< fstream >、< sstream > 等头文件中。这些库提供了丰富的功能,用于在控制台、文件、字符串等之间进行数据的输入和输出。
- ifstreamtream头文件
类型 | 作用 |
---|---|
istream(宽字符为wistream) | 从流读取数据 |
ostream (宽字符为wostream) | 向流写入数据 |
ifstreamtream(宽字符为wifstreamtream) | 读写流 |
- fstream头文件
类型 | 作用 |
---|---|
ifstream(宽字符版本wifstream) | 从文件读取数据。 |
ofstream (宽字符版本wofstream) | 向文件写入数据 |
fstream(宽字符版本wfstream) | 读写文件 |
- sstream头文件
类型 | 作用 |
---|---|
istringstream(宽字符版本wistringstream) | 从string读取数据 |
ostringstream (宽字符版本wostringstream) | 向string写入数据 |
stringstream(宽字符版本wstringstream) | 读写string |
1、IO对象无拷贝和赋值
C++ 的 I/O 对象(如 std::cin、std::cout、文件流对象等)通常不支持拷贝构造函数
和拷贝赋值操作符
,这是因为这些对象通常代表与特定资源(如控制台、文件等)的关联,而这些资源通常不能简单地被复制。
ofstream out1, out2;
out1 = out2;
//错误:IO类不能进行赋值操作
ofstream print(ofstream);
//错误:I/O流对象不应该被复制或返回,可能会导致资源泄露。
out2 = print(out2)
//错误:不能拷贝流对象
如果你想在多个地方使用相同的 I/O 功能,你通常应该传递对 I/O 对象的引用
,而不是试图复制它们。
void processFile(std::ifstream& file)
{
std::string line;
while (std::getline(file, line))
{
// 处理每一行
std::cout << line << std::endl;
}
}
2、条件状态
在C++中,I/O流类(如istream、ostream、fstream、ifstream、ofstream等)都包含条件状态,用于表示流的状态。
这些状态指示了流的当前情况,例如是否遇到了输入/输出错误、是否到达文件末尾(EOF)等。
方法 | 作用 |
---|---|
strm::ifstreamtat | 是一种类型,提供了表达条件状态的完整功能 |
strm::badbit | 用来指出流已经崩溃 |
strm::failbi | 用来指出一个IO操作失败 |
strm::eofbit | 用来指出流到达了文件结束 |
strm::goodbi | 用来指出流未处于错误状态,此值保证为零 |
s.eof() | 若流s的eofbit置位,则返回true |
s.fail() | 若流s的failbit置位,则返回`true |
s.bad() | 若流s的badbit置位,则返回true |
s.good() | 若流s处于有效状态(即没有failbit、badbit或eofbit置位),则返回true |
s.clear() | 将流s中所有条件状态位复位,将流s的状态设置成有效,返回void |
s.clear(flags) | 根据给定的flags标志位,将流s中对应条件状态位复位。flags类型为strm::ifstreamtate。返回void |
s.setstate(flags) | 根据给定的标志位flags,将流s中对应的条件状态位置位,表示发生了错误。flags类型为strm::ifstreamtate。返回void |
s.rdstate() | 返回流s的当前条件状态,返回值类型为strm::ifstreamtate |
3、查询和管理流的状态
#include <ifstreamtream>
#include <fstream>
int main() {
// 尝试打开一个不存在的文件
std::ifstream file("a.txt");
// 尝试从文件中读取一行
std::string line;
std::getline(file, line);
// 检查流的状态
std::cout << std::boolalpha; // 设置为输出bool值为true/false而非1/0
// 检查是否到达文件末尾(EOF)
std::cout << "EOF detected: " << (file.eof() ? "true" : "false") << std::endl;
// 注意:如果文件不存在,eof()可能不会被置位,直到尝试读取并失败
// 检查是否发生I/O操作失败
std::cout << "Failbit set: " << (file.fail() ? "true" : "false") << std::endl;
// 当文件不存在或无法读取时,failbit通常会被置位
// 检查流是否崩溃(如磁盘错误)
std::cout << "Badbit set: " << (file.bad() ? "true" : "false") << std::endl;
// 在这个例子中,badbit通常不会被置位,除非发生严重的I/O错误
// 检查流是否处于良好状态
std::cout << "Goodbit set: " << (file.good() ? "true" : "false") << std::endl;
// 如果failbit、badbit或eofbit中的任何一个被置位,good()将返回false
// 检查流的当前状态(返回一个包含所有置位标志的ifstreamtate值)
std::ifstreamtate state = file.rdstate();
std::cout << "Current state: " << state << std::endl; // 这里可能会输出一些数字,代表状态位的组合
// 重置流的状态
file.clear(); // 清除所有状态标志
// 再次检查流是否处于良好状态
std::cout << "After clear, Goodbit set: " << (file.good() ? "true" : "false") << std::endl;
// 使用特定的标志位重置流状态(这里是一个示例,实际上通常不需要这样做)
file.clear(std::ifstream::failbit); //重置failbit
return 0;
}
4、管理输出缓冲
每个输出流都管理一个缓冲区,用来保存程序读写的数据。
导致缓冲区刷新的原因(数据真正写道输出设备或者文件)的原因有:
-
程序结束,main函数中的return语句。
-
缓冲区满了。
-
使用操作符(如endl、flush、ends)显示刷新。
cout << "hi!" << endl; //输出hi和换行,然后刷新 cout << "hi!" << flush; //输出hi,然后刷新 cout << "hi!" << ends; //输出hi和一个空字符,然后刷新
-
在每个输出操作符之后,我们可以用unitbuf设置流的内部状态,来清空缓冲区。
在C++中,std::ifstream_base::unitbuf 是一个控制标志,用于控制流缓冲区的行为。当设置了 unitbuf 标志时,流会在每次插入操作(如 << 输出操作符)后立即刷新其缓冲区,这意味着输出将不会被缓存,而是直接写入到其关联的设备(如文件、控制台等)中。
std::ofstream file("file.txt"); // 设置unitbuf标志,使每次输出后都刷新缓冲区 file.setf(std::ifstream::unitbuf); // 写入一些文本到文件,每次写入后都会刷新缓冲区 file << "Hello, " << "world!" << std::endl; // 你可以通过unsetf取消unitbuf的设置 // file.unsetf(std::ifstream::unitbuf);
简单方式
cout << unitbuf; //所有输出都会刷新缓冲区 cout << nounitbuf; //返回正常缓冲方式
-
一个流可能被关联到另一个流。这是当读写被关联流时,关联到流的缓冲区会被刷新。
例如:cin和cerr都被关联到cout。因此,读cin或者写cerr都会导致cout缓冲区刷新。
二、文件输入输出
1、fstream中独特的操作。
操作 | 描述 |
---|---|
fstream fstrm; |
创建一个未绑定的文件流。fstream 是头文件 fstream 中定义的一个类型。 |
fstream fstrm(s); |
创建一个 fstream 对象,并打开名为 s 的文件。默认的文件模式(mode )依赖于 fstream 的类型。 |
fstream fstrm(s, mode); |
与前一个构造函数类似,但按指定的 mode 打开文件。mode 可以是如 std::ifstream::in 、std::ifstream::out 、std::ifstream::app 、std::ifstream::binary 等的组合。 |
fstrm.open(s); |
打开名为 s 的文件,并将文件与 fstrm 绑定。默认的文件模式(mode )依赖于 fstream 的类型。 |
fstrm.open(s, mode); |
打开名为 s 的文件,并将文件与 fstrm 绑定,同时指定打开模式 mode 。 |
fstrm.close(); |
关闭与 fstrm 绑定的文件。返回 void 。 |
fstrm.is_open(); |
返回一个 bool 值,指出与 fstrm 关联的文件是否成功打开且尚未关闭。 |
2、文件模式
-
输入模式 (std::fstream::in):
从文件中读取数据。
-
输出模式 (std::fstream::out):
向文件中写入数据。
如果文件已存在,则内容会被清空(除非指定了std::fstream::app)。 -
追加模式 (std::fstream::app):
总是将数据写入文件的末尾,而不会覆盖文件中的现有内容。
通常与std::ofstream一起使用。 -
std::fstream::ate:
打开文件后立即定位到文件末尾。
通常与std::fstream或std::fstream一起使用。 -
二进制模式 (std::fstream::binary):
用于二进制文件的读写。
在文本模式下,平台特定的换行符转换(如\n到\r\n)可能会发生,而在二进制模式下则不会。 -
截断模式 (std::fstream::trunc):
如果文件已存在并且是以输出模式打开的,则该模式会清空文件的内容。
通常与std::ofstream一起使用,并且通常隐含在std::ifstream::out模式中,除非同时指定了std::ifstream::app。 -
更新模式 (std::fstream::in | std::fstream::out):
允许对文件进行读写操作。
通常与std::fstream一起使用。
实例:以out模式打开文件。
ofstream out("file1",ofstream::out);
规则如下:
- 只有对ofstream或fstream对象设定out模式。
- 只有对ifstream或fstream对象设定in模式。
- 只有当out设定时才可以设定trunc模式。
- 只要trunc没被设定,就可以设定app模式。
- 默认情况下,即使我们没有指定trunc,以out模式打开的文件也会被截断。
- ate和binary模式可以用于所有的文件流对象,且可以与任何文件模式组合。
- 每个文件流类型都指定了一个默认的文件模式,ifstream以in模式打开;ofstream以out模式打开;fstream关联的文件默认以in和out模式打开。
三、string流
操作 | 描述 |
---|---|
sstream strm; | strm是一个未绑定的stringstream对象。sstream是头文件sstream中定义的一个类型 |
sstream strm(s) | strm是一个sstream对象,保存string s 的一个拷贝。此构造函数是explicit的。 |
strm.str() | 返回strm所保存的string的拷贝 |
strm.str(s) | 将string s 拷贝到strm中。返回void |
-
sstream strm
#include <sstream> #include <string> #include <iostream> int main() { std::stringstream strm; // strm是一个未绑定的stringstream对象 // 向strm中插入数据 strm << "Hello, "; strm << "World!"; // 提取数据到字符串 std::string result = strm.str(); // 输出结果 std::cout << result << std::endl; // 输出 "Hello, World!" return 0; }
-
使用构造函数创建并初始化std::stringstream对象
#include <sstream> #include <string> #include <iostream> int main() { std::string s = "42"; // 定义一个包含字符串"42"的std::string对象 std::istringstream strm(s); // strm是一个istringstream对象,保存string s的一个拷贝 // 注意:这里使用了istringstream而不是stringstream,虽然stringstream也可以工作,但istringstream更明确 // 从strm中提取整数(尽管s是字符串,但istringstream知道如何将其转换为整数) int number; strm >> number; // strm会尝试将字符串"42"转换为整数,并赋值给number // 输出结果 std::cout << "Extracted number: " << number << std::endl; // 输出 "Extracted number: 42" return 0; }
-
使用strm.str()获取std::stringstream的内容
#include <sstream> #include <string> #include <iostream> int main() { std::stringstream strm; strm << "This is a test string."; // 获取strm中的内容 std::string content = strm.str(); // 输出内容 std::cout << "Content of strm: " << content << std::endl; // 输出 "Content of strm: This is a test string." return 0; }
-
使用strm.str(s)设置std::stringstream的内容
#include <sstream> #include <string> #include <iostream> int main() { std::string s = "Hello, World!"; std::stringstream strm; // 设置strm的内容 strm.str(s); // 提取并输出内容 std::string result; strm >> result; std::cout << "Extracted string: " << result << std::endl; // 输出 "Extracted string: Hello," // 注意:因为strm中的逗号后面有空格,所以上面的提取只得到了"Hello," return 0; }