C++ Primer笔记8---chapter8 IO库

Posted Ston.V

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ Primer笔记8---chapter8 IO库相关的知识,希望对你有一定的参考价值。

1.标准库定义了三种类型的IO,其中包括iostream(istream/ostream),fstream(ifstream/ofstream),sstream(istringstream/ostringstream);其中ifstream和istringstream都继承自istream

另外为了支持使用宽字符的语言,也定义了一组类型来操纵wchat_t类型的数据。

2. IO对象无拷贝或者赋值,进行IO操作的函数通常以引用的方式传递和返回流。(之前就提到过有一些对象无法进行拷贝)

读写IO对象会改变其状态,因此传递和返回引用不能const的。

3. 条件状态:

常见的状态有strm::badbit(发生错误,无法恢复),strm::failbit(发生错误,可以修正,能继续使用),strm::eofbit(读到文件尾),strm::goodbit(当前流正常,此时此值为0)

同样也有s.clear()   s.clear(flags)  s.setstate(flags)  s.rdstate()等相关接口

当读到文件尾时,s.eof()  s.fail()都会返回true(其他的标志也有对应的类似函数来返回)

//复位failbit和badbig,保持其他标志位不变,以位操作的方式设置
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.bafbit);

4. 管理流的输出缓冲:

缓冲区刷新的几种原因:程序正常结束,缓冲区满,使用操纵符如endl来刷新,使用操纵符unitbuf来设置流的状态以清空缓冲区(默认对ceer设置unitbuf,因此写到cerr的内容都是立刻刷新的),读写被关联的输出流导致与之关联的流也随之刷新

几种操纵符:endl,flush,ends,unifbuf

cout<< "hi" <<endl;    //换行并刷新缓冲区
cout<< "hi" <<flush;   //不附带任何字符的刷新缓冲区
cout<< "hi" <<end;     //输出一个空字符并刷新和缓冲区

cout<<unitbuf;    //所有的输出操作后都会进行一次flush来刷新缓冲区
cout<<nounitbuf;  //回到正常的输出方式 

当一个输入流被关联到一个输出流,任何对输入流读取数据的操作都会先刷新关联的输出流

。每个流最多同时关联到一个流,但多个流可以同时关联到同一个ostream

cin.tie(&cout);    //关联cin和cout
//old_tie指向当前cin关联的流(如果有的话)
ostream *old_tie = cin.tie(nullptr);     //cin不在与其他流关联

cin.tie(&cerr);    //读取cin会刷新cerr而不是cout
cin.tie(old_tie);  //重建cin和cout间的正常关联

但是对于流关联的理解,我仍不清楚,刷新输出缓冲区似乎也并不是直接输出的意思,写了测试code也没有预期效果

    cin.tie(&cout);
    int a=0;
    cin>>a;
    cout<<a<<endl;    //我以为会输出两次,结果只输出一次

    cin.tie(&cerr);
    cin>>a;            //我以为会有一次输出,结果并没有
    return 0;

5.文件输入输出:这个都比较熟悉,用的也很多,这里不再赘述,有别人的文章很不错

https://blog.csdn.net/hbqjzx/article/details/86525693

注意1:当使用out方式(写)打开文件时,默认就是trunc的,如果不想源文件内容被清空,就需要追加ofstream::app

注意2:一个流和文件已经绑定时就不能继续绑定其他文件了,我们可以使循环中自动的构造和析构多个流来操作多个文件

//这段code确实写的好,main函数接受一个需要处理的文件列表,这里处理的好
for(auto p=argv+1;p!=argv+argc;++p){    //第一个参数是函数名,所以要argv+1
    ifstream input(*p);
    if(input){
        process(input);
    }else{
        cerr<<"couldn't open: "+ string(*p);
    }//每个循环input都会离开作用域因而被销毁

}

注意3:<<  和 >> 箭头往哪指就是数据往哪流,cin>>s就是数据从标准输入流到s中去 

6.string流:

包括istringstream,ostringstream,stringstream

stringstream特有操作     
sstream ssss是一个未被绑定的stringstream对象。sstream是sstream中定义的一个类型
sstream ss(s)ss保存string s的一个拷贝,构造函数是explicit的
ss.str()  返回strm所保存的string的拷贝
ss.str(s)  将s拷贝到ss当中,返回void

一般的用途是:

1.方便的进行string串(中间有空格)之间的分割

这里书上的code写的好,直接copy上来

#include <string>
#include <iostream>
#include <sstream>
using namespace std;


/*现在如下记录格式,每个人名后面跟着多个电话号码
*可以使用stringstream方便的将其按照空格分割
*
*morgan 20123123 413243241
*drew 41324214
*lee 414324 4231421 5453513
*/

struct PersonInfo{
    string name;
    vector<string>phones;
};

int main(){

    //进行输入,这里输入用流,便于分割
    string line,word;   //保存一行记录和分割开的单词
    vector<PersonInfo>people;
    while(getline(cin,line)){
        PersonInfo info;
        istringstream record(line);     //读取一行数据,record是流对象
        record >> info.name;            //得到name
        while(record >> word)           //得到phones
            info.phones.push_back(word);
        people.push_back(people);
    }

    //进行输出,这里用流输出,方便将多个string整合到一起
    //假定已经有了vaild()和format()函数来验证电话号码和进行格式转换,最终只输出具有有效号码的信息
    for(const auto &entry : people){
        ostringstream formatted,badNums;
        for(const auto &nums : entry.phones){
            if(!valid(nums)){
                badNums << " " << nums;     //将无效的号码写到badnums流中,以空格间隔
            }else
                formatted << " " << format(nums);     //将有效的号码改变格式后写入formatted流中去,以空格间隔
        }

        if(badNums.str().empty())   //没有错误的数
            os << entry.name << " " << formatted.str() <<endl;
        else                        //有错误的就打印出来
            cerr << "input error: " << entry.name << "invlid number(s)" << badNums.str() << endl;
    }
}

2.进行数据类型转换(使用sstream),如int转换为string(在C语言中使用sprintf或者itoa)

#include <string>
#include <iostream>
#include <sstream>
#include <cstdlib>
using namespace std;

int main(){
    stringstream ss;
    string s("123");
    int res=0;
    ss<<s;
    ss>>res;
    cout<<res<<endl;

    //scanf(screen,format,data),sprintf(screen,format,data),实际上screen只是省略了
    //在这里换成char *类型了,这样方便记忆转换顺序
    string s1("234");
    sscanf(s1.c_str(),"%d",&res);   //需要stdio头文件
    cout<<res<<endl;                //数字转C字符串是使用sprintf函数


    res=atoi("345");    //需要有cstdlib头文件
    cout<<res<<endl;    //数字转C字符串是使用itoa函数(integer to ascii)
}

 

以上是关于C++ Primer笔记8---chapter8 IO库的主要内容,如果未能解决你的问题,请参考以下文章

C++ Primer笔记16---chapter13 代码实例

C++ Primer笔记16---chapter13 代码实例

C++ Primer笔记15---chapter13 拷贝控制2

C++ Primer笔记15---chapter13 拷贝控制2

C++ C++ Primer 基础知识笔记

C++ Primer Plus读书笔记