cpp_13_IO流

1  基本概念

1.1  流/流数据(Stream)

        计算机里所有的数据(二进制数据、文本、图形、音视频等)都是字节序列形式的数据,

        它们犹如流水一般,从一个对象流向另一个对象,称为流/流数据。

        分为输出流、输入流。

1.2  输出流(Output Stream)

        数据从内存对象流向表示输出设备(显示器、打印机、磁盘文件等)的对象:

                如:cout << student;

1.3  输入流(Input Stream)

        数据从表示输入设备(键盘、磁盘文件等)的对象流向内存对象:

                如:cin >> student;

1.4  流缓冲区(Steam Buffer)

        介于各种I/O设备和内存对象之间的内存缓冲区。也就是流对象所维护的缓冲区。

        1)当向显示器输出时,数据首先通过流操作符<<,从内存对象进入输出流缓冲区,直到缓冲区满或遇到换行符,才将其中的数据灌注到显示器上显示出来:

                                                        

        2)当从键盘输入时,数据首先进入到键盘缓冲区,直到按下回车键,才将键盘缓冲区中的数据灌注到输入流缓冲区,之后通过流操作符>>,进入内存对象:

                                                        

1.5  流对象(Stream Object)

        表示各种输入输出设备的对象,如键盘、显示器、打印机、磁盘文件等,

        因其皆以流的方式接收或提供数据,故称为流对象。

        三个预定义的标准流对象:

                cin:标准输入设备——键盘

                cout:标准输出设备——显示器

                cerr:标准错误输出设备——显示器(不带缓冲)

1.5  流类(Stream Class)

        用于定义流对象的类。

        流类  istream_withassign  的对象是  cin

        流类 ostream_withassign  的对象是  cout

1.6  流类库(Stream Class Library)

        C++以继承的方式定义了一组流类,并将其作为C++标准库的一部分,提供给用户。

        基于流类库可以构建三种形式的流对象:

                                                面向控制台的I/O流:cin cout cerr

                                                面向文件的I/O流:   自己定义

                                                面向内存的I/O流:   自己定义

        

        

        1)只有蓝色和红色共9个类,针对具体目标执行具体操作

        2)其中蓝色的3个类已经预定义了cin/cout/cerr流对象

        3)实际编程中主要使用红色的6个类实现针对文件和内存的I/O

        4)出于某些原因,所有I/O流类都不支持拷贝构造和拷贝赋值(私有了)

        #include <iostream> // 最熟悉、最常加的头文件

                                          ios   

                                          istream                            ostream                            iostream

                                          istream_withassign         ostream_withassign         iostream_withassign

        #include <fstream>

                                          ifstream 读文件               ofstream  写文件              fstream

        #include <strstream>

                                          istrstream 读内存            ostrstream  写内存           strstream

        #include <sstream>

                                          istringstream  读内存     ostringstream 写内存      stringstream

2  类类型转换为bool

        任何基本类型的数据,都可隐式转换(编译自帮我们转)为bool类型。

        任何符合类型的数据(类对象),需要通过手写类型转换操作符函数转换为bool类型。

        C++程序经常将类对象放在6种bool上下文中:

                                class A { ... };        A a;

                                bool  b  =  a;         // 初始化

                                bool  c;  c  =  a;    // 赋值

                                if(a) {...}                // if语句的判定条件

                                while(a) {...}          // while循环的判定条件

                                for(; a; ) {...}          // for循环的判定条件

                                !a                          // 逻辑反

        一旦出现上述6种bool上下文,编译器会将  类对象  转换为  bool类型。

        C++标准库中封装的流对象(cout、cin等)允许我们将其放在bool上下文中,来判断IO操作(打开文件、读文件、写文件等)是否成功。

// objbool.cpp 类对象 转换为 bool类型
#include <iostream>
#include <cstdio>
using namespace std;

class Integer {
public:
    Integer( int i ) : m_i(i) {
        //【int m_i=i;】
    }
    operator bool( /* const Integer* this */ ) const {
        cout << "Integer类的类型转换操作符函数被调用" << endl;
        return this->m_i;
    }
private:
    int m_i;
};

// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
    Integer ix(8888), iy(0);

    bool a = ix; // 初始化  bool a = ix.operator bool()
    cout << "a=" << a << endl;

    bool b;
    b = iy; // 初值  b=iy.operator bool()
    cout << "b=" << b << endl;

    if( ix ) { // ix.operator bool()
        cout << "if语句判定条件为true" << endl;
    }

    while( ix ) { // ix.operator bool()
        cout << "while循环的判定条件为true" << endl;
        break;
    }

    for( ; ix ; ) { // ix.opereator bool()
        cout << "for循环的第二个判定条件为true" << endl;
        break;
    }

    cout << !ix << endl; // ix.operator bool()
    return 0;
}

3  IO流打开和关闭

3.1  通过构造函数打开I/O流

        打开输入流:        ifstream  (const char* filename,  openmode mode) ;

        打开输出流:        ofstream (const char* filename,  openmode mode) ;

        打开输入输出流:   fstream (const char* filename,  openmode mode) ; 

3.2  打开模式(输出文件流,文件用来写)

        ios::out     适用于ofstream(缺省) / fstream

                        打开文件用于写入,不存在则创建,存在则清空

        ios::app    适用于ofstream / fstream

                        打开文件用于追加,不存在则创建,存在不清空

        ios::trunc  适用于ofstream / fstream

                        打开时清空原内容,(同ios::out)

        ios::binary  适用于ifstream / ofstream / fstream

                          以二进制模式读写

// ofstream.cpp
#include <iostream>
#include <fstream>
using namespace std;

// C++标准库中设计的 ofstream(输出文件流类) 类

// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
    ofstream ofs1("./ofs", ios::out );
    if( !ofs1 ) { // ! ofs1.operator bool()
        cerr << "ofs1流对象状态错误--打开文件失败" << endl;
    }
    ofs1 << 1234 << ' ' << 56.78 << ' ' << "hello" << '\n';
    if( !ofs1 ) { // ! ofs1.operator bool()
        cerr << "ofs1流对象状态错误--写文件失败" << endl;
    }
    ofs1.close();

    ofstream ofs2("./ofs", ios::app);
    if( !ofs2 ) { // ! ofs2.operator bool()
        cerr << "ofs2流对象状态错误--打开文件失败" << endl;
    }
    ofs2 << "world" << endl;
    if( !ofs2 ) { // ! ofs2.operator bool()
        cerr << "ofs2流对象状态错误--写文件失败" << endl;
    }
    ofs2.close();
    return 0;
}

3.3  打开模式(输入文件流,文件用来读)

        ios::in        适用于ifstream(缺省) / fstream

                          打开文件用于读取,不存在则失败,存在不清空

        ios::ate      适用于ifstream / fstream

                          打开文件时定位文件(拿到文件大小,方便开缓冲区来接)

        ios::binary  适用于ifstream / ofstream / fstream

                          以二进制模式读写

// ifstream.cpp
#include <iostream>
#include <fstream>
using namespace std;

// C++标准中设计的 ifstream(输入文件流类) 类

// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
    ifstream ifs1("./ofs", ios::in);
    if( !ifs1 ) { // ! ifs1.operator bool()
        cerr << "ifs1流对象状态错误--打开文件失败" << endl;
    }
    int i;  double d;   string s1, s2;
    ifs1 >> i >> d >> s1 >> s2;
    if( !ifs1 ) { // ! ifs1.operator bool()
        cerr << "ifs1流对象状态错误--读文件失败" << endl;
    }
    cout << i << ' ' << d << ' ' << s1 << ' ' << s2 << endl;
    ifs1.close();

    ifstream ifs2("./ofs", ios::ate);
    if( !ifs2 ) { // ! ifs2.operator bool()
        cerr << "ifs2流对象状态错误--打开文件失败" << endl;
    }

    ifs2.seekg(0,ios::beg);                              // 调整读写位置

    int ii; double dd;  string ss1,ss2;
    ifs2 >> ii >> dd >> ss1 >> ss2;
    if( !ifs2 ) { // ! ifs2.operator bool()
        cerr << "ifs2流对象状态错误--读文件失败" << endl;
    }
    cout << ii << ' ' << dd << ' ' << ss1 << ' ' << ss2 << endl;
    ifs2.close();
    return 0;
}

3.4  关闭IO流对象

         IO流对象名.close()

4  IO流对象的状态

4.1  0 1 2 4

        I/O流类对象内部保存当前状态,其值为以下常亮的位或

                        ios::goodbit        0        一切正常 

                        ios::badbit          1        发生致命错误 

                        ios::eofbit           2        遇到文件尾

                        ios::failbit           4        打开文件失败  或  实际读写字节数未达预期

        

4.2  to bool

        I/O流类对象支持到bool类型的隐式转换:

                        发生1 2 4等情况,返回false,否则返回true

                        将I/O流对象直接应用到布尔上下文中,即可实现转换

4.3  复位

        处于14状态的流,在复位前无法工作。

// iostate.cpp
#include <iostream>
#include <fstream>
using namespace std;

// C++标准中设计的 ifstream(输入文件流类) 类

// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {

    ifstream ifs2("./ofs", ios::ate);
    if( !ifs2 ) { // ! ifs2.operator bool()
        cerr << "ifs2流对象状态错误--打开文件失败" << endl;
    }

    int ii; double dd;  string ss1,ss2;
    cout << "--------------------------第一次读取数据------------------------" << endl;
    ifs2 >> ii >> dd >> ss1 >> ss2;
    if( !ifs2 ) { // ! ifs2.operator bool()
        cerr << "ifs2流对象状态错误--读文件失败" << endl;
        cerr << "ifs2具备0状态吗?" << ifs2.good() << ", ifs2具备1状态吗?" << ifs2.bad()
             << ", ifs2具备2状态吗?" << ifs2.eof() << ", ifs2具备4状态吗?" << ifs2.fail() << endl;
        cerr << "ifs2具体的状态值: " << ifs2.rdstate() << endl;
    }
    cout << ii << ' ' << dd << ' ' << ss1 << ' ' << ss2 << endl;
    
    ifs2.clear();  //先clear为正常状态,seekg才工作,后续读取才正常
    ifs2.seekg( 0, ios::beg );
    cout << "--------------------------第二次读取数据------------------------" << endl;
    ifs2 >> ii >> dd >> ss1 >> ss2;
    if( !ifs2 ) { // ! ifs2.operator bool()
        cerr << "ifs2流对象状态错误--读文件失败" << endl;
        cerr << "ifs2具备0状态吗?" << ifs2.good() << ", ifs2具备1状态吗?" << ifs2.bad()
             << ", ifs2具备2状态吗?" << ifs2.eof() << ", ifs2具备4状态吗?" << ifs2.fail() << endl;
        cerr << "ifs2具体的状态值: " << ifs2.rdstate() << endl;
    }
    cout << ii << ' ' << dd << ' ' << ss1 << ' ' << ss2 << endl;
    
    ifs2.close();
    return 0;
}

 5  二进制IO(原样,不做修改)

        读取二进制数据:istream&  istream::read (char* buffer,  streamsize num);

        1)从输入流中读取num个字节到缓冲区buffer中  !!!

        2)返回流对象本身,其在bool上下文中的值,成功(读满)为true,失败(没读满)为false

        3)如果没读满num个字节,函数就返回了,比如遇到文件尾,

             最后一次读到缓冲区buffer中的字节数,可以通过istream::gcount()函数获得。

        

        获取读长度:streamsize  istream::gcount (void);

        1)返回最后一次从输入流中读取的字节数

        写入二进制数据:ostream&  ostream::write (const char* buffer,  streamsize num);

        1)将缓冲区buffer中的num个字节写到输出流中  !!!

        2)返回流本身,其在bool上下文中的值,成功(写满)为true,失败(没写满)为false

// binaryIO1.cpp
#include <iostream>
#include <fstream>
using namespace std;

// C++标准中设计的 / ifstream(输入文件流类) 类

// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
    
    ofstream ofs("./zjw", ios::out);
    if( !ofs ) {
        cerr << "ofs流对象状态错误--打开文件失败" << endl;
    }

    ifstream ifs("./getline", ios::in); 
    if( !ifs ) {
        cerr << "ifs流对象状态错误--打开文件失败" << endl;
    }
    
    char buf[3];
    while(1) {
        ifs.read(buf,3); 
        if( ifs ) {
            // 读满3个字节
            ofs.write(buf,3);
        } else {
            // 没读满3个字节
            int len = ifs.gcount();
            ofs.write(buf,len);
            break;
        }
    }
    ofs.close();
    ifs.close();
    
    return 0;
}

6  非格式化IO(按个、按行)

6.1  字符

        写入字符:ostream&  ostream::put (char ch);

                        一次向输出流写入一个字符,返回流对象本身

        刷输出流:ostream&  ostream::flush (void);

                        将输出流缓冲区中的数据刷到设备上,返回流对象本身

        读取字符:int  istream::get (void);                   // 无参

                        成功返回读到的字符,失败返回EOF

                         istream&  istream::get (char& ch);  // 单参

                        返回输入流本身。其在bool上下文中的值,成功为true,失败或遇到文件尾为false

// noformat.cpp
#include <iostream>
#include <fstream>
using namespace std;

// C++标准中设计的 ofstream(输出文件流类) / ifstream(输入文件流类) 类

// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
    
    ofstream ofs("./noformat", ios::out);
    if( !ofs ) {
        cerr << "ofs流对象状态错误--打开文件失败" << endl;
    }
    for( char c=' '; c<='~'; c++ ) {
        ofs.put(c).flush();
    }
    ofs.close();

    ifstream ifs("./noformat", ios::in);
    if( !ifs ) {
        cerr << "ifs流对象状态错误--打开文件失败" << endl;
    }
    char c;
    // 单参get
    while(1) {
        ifs.get(c);
        if( ifs ) {
            cout << c;
        } else {
            break;
        }
    }
    cout << endl;

    ifs.clear();
    ifs.seekg(0, ios::beg);
    // 无参get
    while(1) {
        c = ifs.get();
        if( c!=EOF ) {
            cout << c;
        } else {
            break;
        }
    }
    cout << endl;
    ifs.close(); 
    return 0;
}

6.2  行

        读取行:istream&  istream::getline (char* buffer,  streamsize num,  char delim = '\n');

        1)读取字符(至定届符)到buffer

        2)若读取了num个字符还未读取定界符,第num个字符设置为空字符'\0',返回(输入流对象

              状态为4  :( )。

        3)如果因为遇到定界符(缺省为'\n')返回(输入流对象状态为0  :)  ),

              定界符被读取并丢弃,追加结尾空字符'\0'

              读指针停在该定界符的下一个位置,即第二行行首。

        4)遇到文件尾,返回(输入流对象状态为6  :(  )。

// getline.cpp 预置:getline文件,aa\n bbbb\n cccccc\n dddddddd\n 0123456789\n
#include <iostream>
#include <fstream>
using namespace std;

// C++标准中设计的 / ifstream(输入文件流类) 类

// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
    
    ifstream ifs("./getline", ios::in);
    if( !ifs ) {
        cerr << "ifs流对象状态错误--打开文件失败" << endl;
    }

    char buf[256];
    while(1) {
        ifs.getline(buf,256,'\n'); 
        if( ifs ) {
            cout << "ifs流对象的状态值: " << ifs.rdstate() << endl; //0
            cout << buf << endl;
        } else {
            cout << "ifs流对象的状态值: " << ifs.rdstate() << endl; //6
            break;
        }
    }
    /*
    ifs.getline(buf,256,'\n'); 
    cout << "ifs流对象的状态值: " << ifs.rdstate() << endl;  //0
    cout << buf << endl;
    
    ifs.getline(buf,256,'\n'); 
    cout << "ifs流对象的状态值: " << ifs.rdstate() << endl;  //0
    cout << buf << endl;

    ifs.getline(buf,256,'\n'); 
    cout << "ifs流对象的状态值: " << ifs.rdstate() << endl;  //0
    cout << buf << endl;

    ifs.getline(buf,256,'\n'); 
    cout << "ifs流对象的状态值: " << ifs.rdstate() << endl;  //0
    cout << buf << endl;

    ifs.getline(buf,256,'\n'); 
    cout << "ifs流对象的状态值: " << ifs.rdstate() << endl;  //0
    cout << buf << endl;

    ifs.getline(buf,256,'\n'); 
    cout << "ifs流对象的状态值: " << ifs.rdstate() << endl;  //   6
    cout << buf << endl;
    */

//  ifs.clear()
    ifs.close();//对象关闭后,会将文件尾2变为文件首0,对象保留4状态。建议先clear()
    cout << "ifs流对象的状态值: " << ifs.rdstate() << endl;  //   4
    
    return 0;
}

7  格式化IO

7.1  流函数(一组成员函数)

        I/O流类(ios)定义的一组用于控制输入输出格式的公有成员函数

        调用这些函数可以改变I/O流对象内部的格式状态,

        进而影响后续输入输出的格式化方式。

        

        

// 流函数 示例代码
#include <iostream>
#include <cmath> // sqrt()
using namespace std;

int main(void){
    cout.precision (10);
    cout << sqrt (200) << '\n';    // 14.14213562
    cout << cout.precision () << '\n' ;    //10

    cout.setf (ios::scientific, ios::floatfield);
    cout << sqrt (200) << '\n';    // 1.4142135624e+01

    cout.width (10);
    cout.fill ('-');
    cout.setf (ios::internal, ios::adjustfield);
    cout.setf (ios::showpos);
    cout << 12345 << '\n';    // +----12345

    return 0;
}

7.2  流控制符(一组全局函数)

        标准库提供的一组特殊的全局函数

        有的带参(在iomanip头文件中声明),有的不带参(在iostream头文件中声明)。

        因可被直接嵌入到输入输出表达式中,影响后续输入输出格式,称为流控制符。

         

// 流控制符 示例代码
#include<iostream>
#include<iomanip>
#include<cmath> // sqrt()
using namespace std;

int main(void){
    cout << setprecision (10) << sqrt (200) << endl;    // 14.14213562
    cout << cout.precision () << endl;    // 10

    cout << scientific << sqrt (200) << endl;    // 1.4142135624e+01

    cout << setw (10) << setfill ('-') << internal << showpos << 12345 << endl;
                                                 // +----12345
    return 0;
}

8  读写指针 和 随机访问

8.1  设置读/写指针位置

        istream&   istream::seekg  (off_type offset,  ios::seekdir origin); // 读指针

        ostream& ostream::seekp (off_type offset,  ios::seekdir origin);  // 写指针

        1)origin表示偏移量offset的起点:

                        iso::beg        从文件的第1个字节

                        ios::cur         从文件的当前位置

                        ios::end        从文件最后一个字节的下一个位置

        2)offset为  负 / 正  表示想文件  头 / 尾  的方向偏移。

        3)读/写指针被移到文件头之前或文件尾之后,则失败。

8.2  获取读/写指针位置

        pos_type   istream::tellg (void);

        pos_type  ostream::tellp (void);

        1)返回读/写指针当前位置  相对于  文件头  的字节偏移量

// binaryIO2.cpp
#include <iostream>
#include <fstream>
using namespace std;

// C++标准中设计的 / ifstream(输入文件流类) 类

// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
    
    ofstream ofs("./zjw", ios::out);
    if( !ofs ) {
        cerr << "ofs流对象状态错误--打开文件失败" << endl;
    }

    ifstream ifs("./getline", ios::ate); //文件尾,用来得到文件大小
    if( !ifs ) {
        cerr << "ifs流对象状态错误--打开文件失败" << endl;
    }
    
    int len = ifs.tellg();  //获取读指针位置->尾,即文件大小
    char buf[len];
    ifs.seekg(0, ios::beg); //设置读指针位置->首
    ifs.read(buf,len);  // 只读1次硬盘 :)
    ofs.write(buf,len); // 只读1次硬盘 :)
    
    ofs.close();
    ifs.close();
    
    return 0;
}

9  内存流(字符串流)

        上述章节为文件流,本章为内存流(字符串流)。

9.1  输出字符串流

        #include <sstream>

        ostringstream  oss;

        oss  <<  1234  <<  ' '  <<  5.67  << 'A'  <<  "BCD" ;  // 类似cout,用来输出 <<

        string os = oss.str() ;

9.2  输入字符串流

        #include <sstream>

        string is ("1234  5.67  ABCD") ;

        istringstream iss (is) ;

        int i ;      double d ;      string s ;

        iss  >>  i  >>  d  >>  s ;                                            // 类似cin,用来输入 >>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值