C++中的IO库概述及操作笔记

来源:互联网 发布:mxnet 安装 windows 编辑:程序博客网 时间:2024/05/17 22:39

C++中的IO库概述及操作笔记

  • C中的IO库概述及操作笔记
    • IO类
      • IO类继承关系
      • 条件状态
      • 输出管理
        • 冲刷缓冲区
        • unitbuf操作符
        • 绑定输入和输出流对象
    • 文件IO
      • 初始化
      • 文件模式
    • string流
    • C风格IO
    • 总结


C++包括两类IO库,一种是基于流形式的IO库,另一种是C风格的IO函数库,下面我会分别来阐述,重点还是放在C++的流形式IO上。

IO类

IO类继承关系

基于流的输入和输出围绕抽象的输入输出设备组织而成,这些抽象输入输出设备包括控制台窗口,文件和内存中的string对象,并且这些类都是模板化的,类的继承形式如下:

C++的IO类继承关系图
C++的IO类继承架构图

在上面的继承关系图中,ios_base是接口类,主要管理格式符号和输入输出异常,通常我们不对其进行直接操作,我们需要使用基类时也是使用basic_ios,用其来管理任意的流操作。不过我们在日常使用中,不会直接使用上图中的类名,而是通过了别名来进行调用,别名的用法如下代码所示,其中以w开头的都是宽字符类型使用的。在IO流的使用中,一定要注意的是不能够使用拷贝构造和赋值语句,因为在基类中这些函数就已经被私有化了。还有两个库iomanip和stdiostream需要关注,前者声明了格式化IO的一些操作,后者用于混合使用C和C++的IO机制。

/* Defined in header <ios> */typedef basic_ios<char>                 ios;typedef basic_ios<wchar_t>             wios;/* Defined in header <streambuf> */typedef basic_streambuf<char>     streambuf;typedef basic_streambuf<wchar_t> wstreambuf;/* Defined in header <fstream> */typedef basic_filebuf<char>         filebuf;typedef basic_filebuf<wchar_t>     wfilebuf;/* Defined in header <sstream> */typedef basic_stringbuf<char>     stringbuf;typedef basic_stringbuf<wchar_t> wstringbuf;/* Defined in header <istream> */typedef basic_istream<char>         istream;typedef basic_istream<wchar_t>     wistream;/* Defined in header <ostream> */typedef basic_ostream<char>         ostream;typedef basic_ostream<wchar_t>     wostream;/* Defined in header <istream> */typedef basic_iostream<char>       iostream;typedef basic_iostream<wchar_t>   wiostream;/* Defined in header <fstream> */typedef basic_ifstream<char>       ifstream;typedef basic_ifstream<wchar_t>   wifstream;typedef basic_ofstream<char>       ofstream;typedef basic_ofstream<wchar_t>   wofstream;typedef basic_fstream<char>         fstream;typedef basic_fstream<wchar_t>     wfstream;/* Defined in header <sstream> */typedef basic_istringstream<char>     istringstream;typedef basic_istringstream<wchar_t> wistringstream;typedef basic_ostringstream<char>     ostringstream;typedef basic_ostringstream<wchar_t> wostringstream;typedef basic_stringstream<char>       stringstream;typedef basic_stringstream<wchar_t>   wstringstream;

我们平时接触最多的还是标准库中已经被预定义的cin(wcin),cout(wcout),cerr(wcerr),clog(clog),他们分别对应着标准输入流,标准输出流,标准错误输出流和标准日志输出流,不过可以通过rdbuf()方法来改变这些流对象的缓冲区,也就改变了这些流对象的标准功能,注意cerr流无缓冲区。

条件状态

在流对象中有一个非常重要的特性是条件状态,通过该特性我们可以了解一个流对象的当前状态以及是否能正常工作,因为当一个错误发生时,该流对象随后的操作就必然会失败。所有的流对象都有如下四种条件状态:goodbit,badbit,eofbit,failbit。goodbit代表流对象正常,badbit代表该流对象崩溃了,不可恢复,eofbit表示该流对象已经处于流的结束位置,failbit表示某次IO操作失败,但该错误可恢复。同时还提供了一些方法如eof(),fail(),bad(),good(),clear(),setstate(),rdstate()等来对条件状态进行判断和操作。我们以一段代码来说明如何使用这些方法:

#include<string>#include<sstream>#include<iostream>using namespace std;int main(int argc, char* argv[]){    string obj{"just a test!"};    stringstream ss(obj);    stringstream::iostate state = ss.rdstate();     /* 读取该流对象中的条件状态 */    ss.setstate(state | stringstream::failbit);     /* 将该流对象的failbit置位 */    string tmp;    while(ss>>tmp)                                  /* 因为错误存在,该流对象不会输入 */    {        cout<<tmp<<endl;    }    if(ss.fail())        cout<<"true"<<endl;    string obj2{"another test!"};    ss.str(obj2);                                    /* 更换该流对象的BUFFER */    ss.clear();                                      /* 使该流对象有效 */    while(ss>>tmp)    {        cout<<tmp<<endl;    }    if(ss.eof())        cout<<"true"<<endl;    return 0;}

结果:
true
another
test!
true

接下来我用一个表格来说明条件状态位与条件判断方法的关系:

ios_base::iostate flags basic_ios accessors eofbit failbit badbit good() fail() bad() eof() operator bool operator ! false false false true false false false true false false false true false true true false false true false true false false true false false false true false true true false true true false false true true false false false false false true true false true false true false true true true false true true true false false true false true false true true true true false true true true false true

     
可见fail()方法和operator !是作用等价的,而operator bool是与fail()方法作用相反。

输出管理

每一个输出流对象都管理一个缓冲区,这是因为IO操作非常耗时,由于该缓冲区的存在,流对象会将输出都先存放在缓冲区中,等到满足下列条件之一满足后再一起输出,在我们的日常编程中,根据需求可能需要对输出时机进行管理,故针对以下条件提供几种管理输出的方法。

  1. 程序正常完成,如return;
  2. 缓冲区已满;
  3. 显性出现操作符endl;
  4. 使用操作符unitbuf设置缓冲区中间状态为空;
  5. 输出流对象与其他输入或输出流对象进行了绑定。

前面两种条件由程序自己判断,下面三种条件可在程序编写过程对输出进行管理。

冲刷缓冲区

cout<<"hi!"<<endl;         /* 跳转下一行 */cout<<"hi!"<<flush;        /* 不添加数据 */cout<<"hi!"<<ends;         /* 末尾添加null */cout.flush();

unitbuf操作符

cout<<unitbuf;         /* 每次输出都冲刷缓冲区 */cout<<nounitbuf;       /* 正常缓冲 */

绑定输入和输出流对象

cin.tie(&cout);         /* 将标准输入和标准输出进行绑定,每次输入都将冲刷输出缓冲区 */                        /* 一个流对象只能绑定一个流对象,当一个流对象可以被多个流对象绑定 */

文件IO

初始化

文件流对象有两种初始化形式,第一种是利用构造函数,直接在实例化一个文件流对象时就赋给其一个文件名,第二种则是使用open()方法,在实例化一个文件流对象后再显式调用open()方法打开一个文件,文件流对象在作用域结束时会自动调用析构函数释放该文件,但是如果在作用域内需要另外一个文件流对象去对同一个文件进行操作,则需要第一个文件流对象显式调用close()方法释放该文件,一个文件同一时间只能被一个文件流对象访问。利用is_open()方法可以判断一个文件流对象是否打开了一个文件。

#include <string>#include <fstream>#include <iostream>using namespace std;int main(int argc, char* argv[]){    string filename = "example.123";    fstream fs;    fs.open(filename);    if(!fs.is_open())                      /* 文件可能不存在 */    {       fs.clear();                         /* 清除失败状态 */       fs.open(filename, ios::out);        /* 创建文件 */       fs.close();       fs.open(filename);    }    cout<<boolalpha;    cout<<"fs.is_open() = "<<fs.is_open()<<endl;    cout<<"fs.good() = "<<fs.good()<<endl;}

结果:
true
true

文件模式

打开模式,可进行相或运算 含义 in 用于输入 out 用于输出,默认以trunc形式 app 用于添加,每次,与trunc互斥 ate 打开后定位在末尾,一次,不能单独使用 trunc 打开时丢弃文件内容 binary 二进制形式,不能单独使用

string流

string流对象是使用string对象作为缓冲区,可以将string中的内容输出或输入到其他类型的对象中。string流对象可以在实例化时用一个string对象进行构造,也能够在实例化后用str()方法载入新的string对象,其他操作方法与文件流对象类似。string流对象的一个优点是自动进行类型转化:

#include<string>#include<sstream>using namespace std;int main(int argc, char* argv[]){    string str{"123 5.89 false"};    stringstream ss(str);    int inttmp;    double doubletmp;    bool booltmp;    ss>>inttmp;    ss>>doubletmp;    ss>>booltmp;    cout<<inttmp<<'\t'<<doubletmp<<'\t'<<booltmp<<endl;    return 0;}

结果:
123 5.89 0

C风格IO

C风格的IO由一系列函数组成,对FILE结构指针指向的文件或者对象来进行操作,该系列函数都在<\cstdio>中被声明。
文件访问类
fopen、freopen、fclose、fflush、fwide、setbuf、setvbuf

直接输入输出类
fread、fwrite

非格式化输入输出
字节/字符串
fgetc/getc、fgets、fputc/putc、fputs、getchar、gets、putchar、puts、ungetc

格式化输入输出
字节/字符串
scanf/fscanf/sscanf、vscanf/vfscanf/vsscanf、printf/fprintf/sprintf/snprintf、vprintf/vfprintf/vsprintf/vsnprintf

文件位置
ftell、fgetpos、fseek、fsetpos、rewind

错误处理
clearerr、feof、ferror、perror

文件操作
remove、rename、tmpfile、tmpnam

总结

  • iostream类处理到控制台的IO
  • fstream类处理文件的IO
  • stringstream类处理到内存中string对象的IO

fstream和stringstream类继承于iostream类,ifstream和istringstream类继承于istream类,ofstream和ostringstream类继承于ostream类。每个IO对象都有一系列的条件状态来表明是否能够被执行。如果一个错误发生,流对象将不会有进一步的输入或者输出,直到问题被解决,状态复位。同时,标准IO库也提供了一系列的函数来设置和测试这些状态。

PS:本文还有一批IO操作符,流对象的缓冲以及更为具体的IO流对象的方法没有涉及,所以本文仍然会继续进行补充和拓展。本文的不足之处和因为笔者知识缺陷导致的错误还请读者指出来,谢谢

0 0