《c++ primer》 第8章 IO库 学习笔记

1.IO类

#include <iostream>
istream//从流中读取数据
ostream//从流中写入数据
iostream//读写流

#include <fstream>//文件
ifstream
ofstream
fstream

#include <sstream>//字符串
istringstream
ostringstream
iostringstream

fstream和sstream都继承于iostream,所以iostream有的操作他们都有

另外流前面加字母w表示针对宽字符。


2.IO对象无拷贝和赋值

ostream o1,o2;
o1 = o2;
ostream o3(o1);
错误,不能拷贝和赋值

因为流不能拷贝和赋值,所以流作为函数参数或者是返回值必须是引用类型,且读写一个IO对象会改变其状态,

因此传递和返回的引用不能是const的

就好像

const成员函数不可改变对象内容,同时也不可通过函数返回值改变对象内容。 所以返回指向对象的指针

或引用都会被编译器拦住的。 const指针或const引用不会改变对象内容,所以可以返回。


3.IO条件状态

strm代表一种IO类型,流处于好的状态时这些bit位全部处于0。

strm::iostate   iostate一种机器相关类型,提供了表达条件状态的完整功能
strm::badbit    指出流已经崩溃
strm::failbit   指出IO操作失败
strm::eofbit    指出流达到了文件的末尾
strm::goodbit   指出流处于未错误状态

s.eof()          若流处于eofbit,返回true
s.fail()         若流处于failbit,返回true
s.bad()          若流处于badbit,返回true
s.good()         若流处于有效状态,返回true
s.clear()        将流所有状态恢复,设置为有效,返回void
s.clear(flag)    将指定bit位恢复,设置为有效,返回void。flag类型为strm::iostate
s.setstate(flag) 根据给定的bit标志位,将流s中对应条件状态位置位。flag类型为strm::iostate
s.rdstate()      读取当前的状态

例子:

#include <iostream>
#include <fstream>

using namespace std;

istream& read(istream& is)
{
    int i;
    while(is >> i && i != EOF)         
    {
        cout << i << endl;
    }
    cout << "eof:" << is.eof() << endl;    
    cout << "fail:" << is.fail() << endl;
    cout << "bad:" << is.bad() << endl;
    cout << "good:" << is.good() << endl;
    cout << "rdstate " << is.rdstate() << endl;
    
    cout << endl;

    //is.clear();和下面等价
    is.clear(is.rdstate() & ~is.failbit & ~is.eofbit);
    //is.setstate(is.eofbit);设置了后,rdstate()结果是2

    cout << "eof:" << is.eof() << endl;
    cout << "fail:" << is.fail() << endl;
    cout << "bad:" << is.bad() << endl;
    cout << "good:" << is.good() << endl;

    cout << endl;
    cout << "rdstate " << is.rdstate() << endl;
    
    return  is;
}

int main()
{
    read(cin);

    return 0;
}
结果,上面是未注释掉is.setstate(is.eofbit);的结果

下面是注释掉的结果

《c++ primer》 第8章 IO库 学习笔记_第1张图片

还有一种可以保存流当前状态,清空,然后在恢复

auto old_state = is.rdstate( );
is.clear( );
is.setstate(old_state);


4.管理缓冲区

#include <iostream>
using namespace std;

int main()
{
    cout << "hi!" << endl;
    cout << "hi!" << ends;   //缓冲区插入一个字符,然后刷新
    cout << "hi!" << flush;  //刷新缓冲区,不输出任何字符
}

5.关联输入输出缓冲区

任何试图从输入流读数据的操作都会先刷新关联的输出流。

#include <iostream>
using namespace std;

int main()
{
    int i;
    cin >> i;                    //刷新cout 和 cerr;
    ostream *old_str;    
    old_str = cin.tie(nullptr);  //解开流,传递一个空指针。返回旧解
    ostream &os = cout;          //新建一个流
    cin.tie(&os);                //绑定这个新流
    cin >> i;
    cin.tie(old_str);            //恢复以前的状态
}



6.文件的输入和输出

头文件定义了三种IO

ifstream从文件中读取数据

ofstream向一个文件写数据

fstream可以读写

//open:流用open和文件关联起来,如果open失败,failbit会被置位。因为open可能会失败,所以检查流是个好习惯
//close:当将流文件关联到另外一个文件时,必须用close关闭当前的文件。

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

std::istream& read(std::istream& is, std::string& s) //不写引用就成为了副本
{
    is >> s;
    return is;
}

std::ostream& print(std::ostream& os, std::string& s)//可以向iostream传递fstream,因为是派生类,不过对文件最好使用fstream
{
    os << s;
    return os;
}


int main(int argc, char *argv[])
{
    std::ifstream input(argv[1]);
    std::ofstream output(argv[2]);

    std::string s;
    read(input, s);
    std::cout << s << std::endl;
    print(output, s);
    std::string ss;
    //std::getline(ss, argv[1]);  读取一行
    std::cin >> ss;

    std::string ifile = "file2";
    std::ifstream in(ifile);    //新建一个文件流,并且初始化,初始化意味着open()
    std::ofstream out;
    out.open(ifile + ".copy");  //调用open()
    if(out);                    //如果流没错
    out << ss;
    out.close();                //没有close()下面error         close关闭流,一个流不能重复打开多个文件
    out.open("file");
    if(out)
        std::cout << out.good() << endl;
    else
        std::cerr << "error" << std::endl;
} 

课后题8.5

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

int main(int argc, char *argv[])
{
    string s;
    fstream input(argv[1]);
    vector<string>ivec;

    while(!input.eof())//如果去掉if(input.fail())末尾会多输出一次
    {
        input >> s;
        if(input.fail())
        {
            cout << "fail " << endl;
            break;
        }
        ivec.push_back(s);
    }

    for(string &s : ivec)
    {
        cout << s << endl;
    }
}
没有if(input.fail( ))检查时,末尾总是会多输出一次文件末尾的内容。

看代码,文件每次读取到s中,然后push_back(s),当文件读取到最后一次时(它自己不知道读取到最后了),push_back(s)后再次读取,

发现到达末尾了,此时s中依然存着上一次的内容,push_back(s)后退出循环,所以最后一行s被push了两次,输出时当然输出两遍了。


7.文件模式

fstream fs("文件名", mode); //mode就是文件模式
 
in         以读的方式打开
out        以写的方式打开     
app        每次写操作前均定位到末尾
ate        打开文件后立刻定位到文件末尾
trunc      截断文件
binary     以二进制方式IO
<1.只对ofstream或fstream设置out模式,out模式打开会丢弃已有数据

<2.只对ifstream或fstream设置in模式

<3.只有当out也被设定时才可以设定trunc模式

<4.只要trunc模式没被设定,就可以设定app模式,在app模式下即使没有规定out也默认为out模式

<5.没有指定trunc,以out打开也会截断文件,除非指定app,或同时打开in模式

<6.ate和binary可以用于任何类型的文件流对象,且可以和其他任何文件模式组合。

<7.每次调用open都可以指定文件模式。


8.sstream流

三个IO
istringstream
ostringstream
stringstream
特有操作
sstream strm;       strm是一个未绑定的stringstream对象
sstream strm(s);    strm是一个sstream对象,保存s的一个拷贝,此构造函数是explicit的
strm.str();         返回strm所保存的string的拷贝
strm.str(s);        将string s拷贝到strm中,返回void

使用istringstream流

istringstream读字符串时遇见空格算一个字符串,就可以读一个句子的单词。

#include <iostream>
#include <vector>
#include <sstream>
#include <string>
#include <fstream>

using namespace std;

class record
{
    public:
        string name;
        vector<string>phone;
};

int main(int argc, char *argv[])
{
    ifstream input(argv[1]);
    string s;
    vector<record>Info;
    while(getline(input, s))
    {
        record re;
        istringstream is(s);
        is >> re.name;
        cout << "eof" << is.eof() << endl;
        while(!is.eof())  //while(is >> s)另外一种方法,不用测试eof,且注意eof成立为1
        {
            string s;
            is >> s;
            re.phone.push_back(s);
        }
        Info.push_back(re);
    }

    for(record &rec : Info)
    {
        cout << rec.name << " ";
        for(string &ph : rec.phone)
        {
            cout << ph << " ";
        }
        cout << endl;
    }
}

使用ostringstream流

ostringstream流里面可以保存字符串的拷贝。需要时输出。

比如检查一个字符串是否正确,正确的保存到流里,不正确的不保存,等到结束时输出全为正确的。

    string st = "asdasdasdasd";
    string st1;
    ostringstream os;
    os << " " << st<< endl;         //都写到了流里面
    os << " " << st << endl;
    cout << os.str() << endl;       //输出字符串流里面保存的字符串,返回字符串的拷贝
    os.str("");                     //使用前清空
    os.str(st);
    cout << os.str() << endl;

小结:

1.iostream处理控制台IO

2.fstream处理命名文件IO

3.stringstream完成内存string的IO

4.类fstream和sstream都继承自iostream,所以iostream的操作这些类也可以。

5.每个流都有一组条件状态,保证流正确无误的使用就要保证流的状态正确无误

6.为什么可以while(cin)来判断流状态,因为cin对象中重载了bool转换操作,如operator bool(),所以cin才能够参加布尔运算.

你可能感兴趣的:(《c++ primer》 第8章 IO库 学习笔记)