C++ IO库

IO就是input和output和起来的缩写,在C++语言当中读写文件,控制台操作或者是磁盘以及内存读写都可以通过C++当中IO库来实现。网络上有许多关于io库的介绍,本文主要记录自己在实验当中情况、查阅资料的理解,以及对其他之前的高人博客内容的总结,参考资料是C++ primer第5版。

IO类

书中首先介绍了3个头文件,分别是

  • < iostream > 定义了读写流的基本类型
    • —->istream 从流当中读取数据
    • —->wistream 宽字符版本,从流当中读取数据
    • —->ostream 向流当中写入数据
    • —->wostream 宽字符版本,向流当中写入数据
    • —->iostream 读写流
    • —->wiostream 宽字符读写流
  • < fstream >定义了读写命名文件的类型
    • —->ifstream,wifstream 从文件读取数据
    • —->ostream,wofstream 向文件写入数据
    • —->fstream,wfstream 读写文件
  • < sstream >
    • —->istringstream,wistringstream 从string读取数据
    • —->ostringstream, wostringstream 向string写入数据
    • —->stringstream,wstringstream 读写string

w字母开头的是为了支持使用宽字符的语言,wcin、wcout、wcerr分别对应cin、cout、cerr。

还没有用过宽字符型,自己敲了两下。宽字符型用cin和cout果然不好使,呵呵

#include 
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    wchar_t c;
    wcin>>c;
    wcout<return 0;
}

###IO类型之间的关系
C++的标准库可以忽略掉不同类型在流操作这件的差异,这是用过继承实现的。例如,ifstream和istringstream都继承自istream。类似的继承如下,图片来自http://en.cppreference.com/w/cpp/io
C++ IO库_第1张图片
因为继承关系的性质,可以把一个派生类对象当做基类对象使用,那么就可以使用ifstream和istringstream像使用cin一样去使用上面的这些对象。
比如经常会用到读取文件,写入文件的操作,以及把int转换成string的操作。

#include 
using namespace std;
fstream in;
int main()
{
    ios::sync_with_stdio(false);
    int a;
    in.open("data.txt");//里面放了一个整数
    in>>a;//像cin一样去使用
    cout<//输出读出的数据
    in.close();//关闭文件流
    return 0;
}

下面的代码是字符串转换成整数,然后加上1

#include 
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    stringstream ss;
    string s;
    int a;
    cin>>s;
    ss<//stringstream可以像cin一样去使用,得益于继承的机制
    ss>>a;
    cout<1<return 0;
}

###IO对象没有拷贝或赋值
不能够将形参或返回类型设置为流类型,进行io操作的函数通常以引用的方式传递和返回流,读写io对象会改变其状态,因此传递和返回的引用不能是const的。
书上代码

ofstream out1,out2;
out1=out2;//错误,不能赋值
ofstream print(ofstream);//错误,不能初始化
out2=print(out2);//错误,不能拷贝

IO的条件状态

IO的条件状态
strm::iostate iostate是一种极其相关的类型,提供了表达条件状态的完整功能
strm::badbit strm::iostate 类型的值,用于指流已经崩溃
strm::failbit strm::iostate 类型的值,用于指出失败的 IO 操作
strm::eofbit strm::iostate 类型的值,用于指出流已经到达文件结束
s.eof() 如果设置了流s的eofbit 值,则该函数返回 true
s.fail() 如果设置了流s的failbit或badbit值,则该函数返回 true
s.bad() 如果设置了流s的badbit值,则该函数返回 true
s.good() 如果流 s 处于有效状态,则该函数返回 true
s.clear() 将流s中所有条件状态复位,将流的状态设置为有效。返回void
s.clear(flags) 根给定的flags标志位,将流s中对应条件状态位复位。flags的类型为iostate。返回void
s.setstate(flags) 根据flags标志位,将流s中对应条件状态位职位。flags的类型为iostate。返回void
s.rdstate() 返回流 s 的当前条件,返回值类型为 strm::iostate

其中iostate有四个值,分别是failbit, eofbit, badbit和goodbit
iostate拥有这四种状态,这四种状态是iostate的一个具体值。
通过代码输出结果

void state()
{
    cout<<istream::goodbit<;
    cout<<istream::badbit<;
    cout<<istream::eofbit<;
    cout<<istream::failbit<;
}

如果代码里面仅仅输出状态,得到的结果是0,1,2,4。分别为iostate对应二进制的状态,这些状态可以用”|”运算符来组合使用。
000
001
010
100

找一个字符串流测试一下,代码来自cpp官网

#include 
using namespace std;

void print_state (const std::ios& stream) {
    std::cout << " good()=" << stream.good();    
    std::cout << " eof()=" << stream.eof();    
    std::cout << " fail()=" << stream.fail();    
    std::cout << " bad()=" << stream.bad();   
}

int main () {
    std::stringstream stream;

    stream.clear (stream.goodbit);    
    std::cout << "goodbit:"; print_state(stream); std::cout << '\n';

    stream.clear (stream.eofbit);
    std::cout << " eofbit:"; print_state(stream); std::cout << '\n';

    stream.clear (stream.failbit);
    std::cout << "failbit:"; print_state(stream); std::cout << '\n';

    stream.clear (stream.badbit);
    std::cout << " badbit:"; print_state(stream); std::cout << '\n';

    return 0;
}

结果可以运行一下看看,输出如下

goodbit: good()=1 eof()=0 fail()=0 bad()=0
 eofbit: good()=0 eof()=1 fail()=0 bad()=0
failbit: good()=0 eof()=0 fail()=1 bad()=0
 badbit: good()=0 eof()=0 fail()=1 bad()=1

这里先拿cin写一个测试的例子,书上举例说给一个整数读入字符串会出现错误,那会出现什么错误呢?

#include 
using namespace std;
int main ()
{
    int val;
    cout<<cin.rdstate()<//最初的cin的状态
    cout<<cin.good()<<cin.eof()<<cin.fail()<<cin.bad()<//输出cin各项状态值
    cin>>val;//输入一个123试试
    cout<<cin.good()<<cin.eof()<<cin.fail()<<cin.bad()<cin.clear();//清空,重置cin的状态为iostream::good
    cin>>val;//输入qwe
    cout<<cin.good()<<cin.eof()<<cin.fail()<<cin.bad()<return 0;
}

首先输出
0
1000
输入123后
1000
输入qwe后
0010

现在增加一个重新置位

#include 
using namespace std;
int main ()
{
    int val;
    cout<<cin.rdstate()<//最初的cin的状态
    cout<<cin.good()<<cin.eof()<<cin.fail()<<cin.bad()<//输出cin各项状态值
    cin>>val;//输入一个123试试
    cout<<cin.good()<<cin.eof()<<cin.fail()<<cin.bad()<cin.clear();//清空,重置cin的状态为iostream::good
    cin>>val;//输入qwe
    cout<<cin.good()<<cin.eof()<<cin.fail()<<cin.bad()<//  cin.clear();//清空重置后结果
    cin.clear(std::iostream::goodbit);//效果同上
    cout<<cin.good()<<cin.eof()<<cin.fail()<<cin.bad()<return 0;
}

清空后输出结果就是1000了,因为cin被重新置位成了good

现在贴一个检查文件是否存在的,如果文件存在读入一个整数。如果文件不存在,输出错误,如果读到文件尾部,输出end of file!

#include 
using namespace std;
int main ()
{
    ifstream is;
    int a;
    is.open ("data.txt");
    if ( (is.rdstate() & ifstream::failbit ) != 0 )  
    {
        cerr << "Error opening !"; return 0;
    }
    else
    {
        while(!is.eof())
        {
            is>>a;
            if(is.good())
                cout<else
                cerr<<"end of file!"<

首先先判断打开的文件是否存在,如果不存在则输出error opening!
while(!is.eof())的应用,如果is读取到文件尾部会设置eofbit状态,但是不会返回0,而是继续读取,所以如果读到了结尾还会在输出一次最后一行。这里用判断is.good()的方式来判断流是否正确。

刷新缓冲区
缓冲区就是把输入输出的放到内存当中,使用时再从内存当中取出。这样做的目的可以让cpu和硬件设施比如打印机之类的东西协调。

导致缓冲刷新的原因:

  • 程序正常结束
  • 缓冲区满了
  • 使用endl(经常当换行使用了 呵呵)
  • 使用unitbuf
  • 一个输出流被关联到另一个流

如何刷新?

  1. endl操作符,用于输出一个换行符并刷新缓冲区
  2. flush操作符,用于刷新流,但不在输出中添加任何字符
  3. ends操作符,在缓冲区中插入空字符null,然后刷新
  4. 刷新所有输出,unitbuf操作
  5. 关联输入输出流
#include 
using namespace std;
int main ()
{
    cout<<"hi"<cout<<"hi"<cout<<"hi"<cout<//全刷新
    return 0;
}

如何关联输入输出?使用tie函数,它返回指向输出流的指针。

#include 
using namespace std;
int main()
{
    ofstream out("data.txt");//放这个文件里面写东西
    cin.tie(&out);//把cin关联到out上面
    out<<123;//写入文件
    int val;//
    cin>>val;//只要使用cin,就会刷新输出流,这样123就被写进data.txt
    return 0;
}

to be continue~

你可能感兴趣的:(c/c++语言学习)