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、管理输出缓冲

每个输出流都管理一个缓冲区,用来保存程序读写的数据。
导致缓冲区刷新的原因(数据真正写道输出设备或者文件)的原因有:

  1. 程序结束,main函数中的return语句。

  2. 缓冲区满了。

  3. 使用操作符(如endl、flush、ends)显示刷新。

    cout << "hi!" << endl;  //输出hi和换行,然后刷新
    cout << "hi!" << flush; //输出hi,然后刷新
    cout << "hi!" << ends;  //输出hi和一个空字符,然后刷新
    
  4. 在每个输出操作符之后,我们可以用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;  //返回正常缓冲方式
    
  5. 一个流可能被关联到另一个流。这是当读写被关联流时,关联到流的缓冲区会被刷新。

    例如: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::instd::ifstream::outstd::ifstream::appstd::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);

规则如下:

  1. 只有对ofstream或fstream对象设定out模式。
  2. 只有对ifstream或fstream对象设定in模式。
  3. 只有当out设定时才可以设定trunc模式。
  4. 只要trunc没被设定,就可以设定app模式。
  5. 默认情况下,即使我们没有指定trunc,以out模式打开的文件也会被截断。
  6. ate和binary模式可以用于所有的文件流对象,且可以与任何文件模式组合。
  7. 每个文件流类型都指定了一个默认的文件模式,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
  1. 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;  
    }
    
  2. 使用构造函数创建并初始化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;    
    }
    
  3. 使用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;  
    }
    
  4. 使用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;  
    }
    
posted @ 2024-05-25 14:44  baobaobashi  阅读(7)  评论(0编辑  收藏  举报