【C++】I/O流类库

I/O

数据的输入和输出(input/output简写为I/O),对标准输入设备和标准输出设备的输入输出简称为标准I/O。对在外存磁盘上的文件输入输出简称为文件I/O。对内存中指定的字符串存储空间的输入输出简称为串I/O。

数据输入输出的过程,可以形象地看成流。从流中获取数据地操作称为“提取”(输入操作)。向流中添加数据的操作称为“插入”(输出)操作。

流类库继承体系

在这里插入图片描述

  • 流类库具有两个平行的基类:streambuf和ios类,所有流类均以两者之一作为基类。
  • streambuf类提供对缓冲区的低级操作:设置缓冲区、对缓冲区指针操作、向缓冲区存/取字符。
  • ios_base、ios类记录流状态,支持对streambuf的缓冲区输入/输出的格式化或非格式化转换。
  • strstreambuf:使用串保存字符序列。扩展streambuf在缓冲区提取和插入操作。
  • filebuf:使用文件保存字符序列。包括打开文件、读/写、查找字符。

四个输入输出对象

C++为用户进行标准I/O操作定义了四个类对象:cin,cout,cerr和clog。cin为istream流类的对象,代表标准输入设备键盘,后三个为ostream流类的对象。cout代表标准输出设备显示器。cerr和clog含义相同,均代表错误信息输出设备显示器。

ostream流的操作

1.operator<<

为什么可以连续使用<<操作?<<返回值是一个ostream的引用,可以连续使用<<重载。<<提供了基本类型的重载。

2.put

put操作输出单个字符,返回一个ostream的引用。

cout.put('A').put('B').put('C');

3.write

write操作输出缓冲区指定的长度,返回一个ostream的引用。

char buf[] = "testing!!!";
cout.write(buf, 8);

istream流的操作

1.operator>>

返回值是一个istream的引用,可以连续使用>>重载。>>提供了基本类型的重载。

2.get

get操作读取单个字符,返回一个整数为字符的ASCII码。

int ch = cin.get();
cout << ch << endl;

还可以接收一个引用。

char ch, ch2;
cin.get(ch).get(ch2);
cout << ch << " " << ch2 << endl;

3.getline

getline操作读取一行(遇到回车键)。返回istream对象的引用。getline()操作与>>的区别:getline获取一个整行,>>遇到空格停止。

char buf[10] = {
    
     0 };
cin.getline(buf, 10);
cout << buf << endl;

char buf2[10] = {
    
     0 };
cin >> buf2;
cout << buf2 << endl;

4.read

read操作返回一个istream对象的引用,对空白字符照度不误,读取完指定数量的字符才结束。

char buf[10] = {
    
     0 };
cin.read(buf, 5);
cout << buf << endl;

5.peek与putback

peek查看而不读取;putback将一个字符添加到流。

char c[10], c2, c3;
c2 = cin.get();
c3 = cin.get();
cin.putback(c2);
cin.getline(&c[0], 9);
cout << c << endl;

文件流

ofstream,由ostream派生而来,用于写文件。
ifstream,由istream派生而来,用于读文件。
fstream,由iostream派生而来,用于读写文件。

打开文件

说明了流对象之后,可使用函数open()打开文件。文件的打开即是在流与文件之间建立一个连接。函数原型:

void open(const char* filename,int mode = ios::out,int prot = _SH_DENYNO);

mode表示文件打开模式,prot保护模式。判断文件打开成功有5种方式:

#include <iostream>
#include <fstream>
#include <cassert>
using namespace std;

int main()
{
    
    
    ofstream fout;
    fout.open("test.txt");
    //1
    if (fout.is_open())
    {
    
    
        cout << "succ" << endl;
    }
    else
        cout << "failed" << endl;
    //2
    if (fout.good())
    {
    
    
        cout << "succ" << endl;
    }
    else
        cout << "failed" << endl;
    //3
    if (fout)
    {
    
    
        cout << "succ" << endl;
    }
    else
        cout << "failed" << endl;

    //4
    if (!fout)
    {
    
    
        cout << "failed" << endl;
    }
    else
        cout << "succ" << endl;

    //推荐使用断言方式
    assert(fout);

    fout.close();
    return 0;
}

文件打开模式

打开方式 描述
iso::in 打开一个供读取的文件(ifstream的默认值)
iso::out 打开一个供写入的文件(ofstream的默认值)
iso::app 在写之前找到文件尾
iso::ate 打开文件后立即将文件定位在文件尾
iso::trunc 废弃当前文件内容
iso::nocreate(已不再支持) 如果要打开的文件并不存在,那么以此函数调用open()函数将无法进行
iso::noreplace(已不再支持) 如果要打开的文件已存在,试图用open()函数打开时将返回一个错误
iso::binary 以二进制的形式打开一个文件,默认为文本文件

保护模式

#define _SH_DENYRW      0x10    /* deny read/write mode */拒绝对文件进行读写
#define _SH_DENYWR      0x20    /* deny write mode */拒绝写入文件
#define _SH_DENYRD      0x30    /* deny read mode */拒绝文件的读取权限
#define _SH_DENYNO      0x40    /* deny none mode */读取和写入许可
#define _SH_SECURE      0x80    /* secure mode */共享读取,独占写入

文件打开的几点说明

  • 文件打开也可以通过构造函数打开,例如:
ofstream fout("test.txt,ios::out");
  • 文件的打开方式可以为上述的一个枚举常量,也可以为多个枚举常量构成的按位或表达式。

  • 使用open成员函数打开一个文件时,若由字符指针参数所指定的文件不存在,则建立该文件。

  • 当打开方式中不含有ios::ate或ios::app选项时,则文件指针被自动移到文件的开始位置,即字节地址为0的位置。

  • 从效果上看ofstream指定out模式等同于指定了out和trunc模式。

  • 默认情况下,fstream对象以in和out模式同时打开。

  • 当文件同时以in和out打开时不会清空。

  • 如果只使用out模式,而不指定in模式,则文件会清空现有数据。

  • 如果同时指定了out与app,不会清空。

  • 如果打开文件时指定了trunc模式,则无论是否同时制定了in模式,文件同样会被清空。

流状态

打开方式 描述
ios::goodbit 一切正常,没有错误发生,也没有输入结束
ios::eofbit 输入结束
ios::failbit I/O操作失败,主要原因是非法数据(例如试图读数字时遇到字母)。流可以继续使用,输入结束时也将设置failbit位。
ios::badbit 发生了(或许是物理上的)致命性错误。流将不能使用。

对应于这个标志字各状态位,ios类还提供了以下成员函数来检测或设置流的状态:

bool rdstate();             //返回流的当前状态标志字
bool eof();                 //返回非0值表示到达文件尾
bool fail();                //返回非0值表示操作失败
bool bad();                 //返回非0值表示出现错误
bool good();                //返回非0值表示流操作正常
bool clear(int flag=0);     //将流的状态设置为flag

为提高程序的可靠性,应在程序中检测I/O流的操作是否正常。当检测到流操作出现错误时,可以通过异常处理来解决问题。

文件关闭

每个文件流类中都提供有一个关闭文件的成员函数close()。功能:当打开的文件操作结束后,就需要关闭它,使文件流与对应的物理文件断开联系,并能够保证最后输出到文件缓冲区中的内容,无论是否已满,都将立即写入到对应的物理文件中。文件流对应的文件被关闭后,还可以利用该文件流调用open成员函数打开其他的文件,最好先clear 一下。

文件读写

1.>>和<<

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

int main()
{
    
    
    ofstream fout("test.txt");
    fout << "abcd" << " " << 200;
    fout.close();

    ifstream fin("test.txt");
    string s;
    int n;
    fin >> s >> n;
    cout << s << " " << n << endl;

    return 0;
}

2.put和get

#include <iostream>
#include <fstream>
#include <string>
#include <cassert>
using namespace std;

int main()
{
    
    
    ofstream fout("test2.txt");
    char ch;

    assert(fout);

    for (int i = 0; i < 26; i++)
    {
    
    
        ch = 'A' + i;
        fout.put(ch);
    }
    fout.close();

    ifstream fin("test2.txt");
    while (fin.get(ch))
    {
    
    
        cout << ch;
    }

    return 0;
}

3.read和write

后面二进制文件读写介绍。

4.文本模式与二进制模式打开区别

如果以文本打开文件,写入字符的时候,遇到\n会作转换。windows平台\n会转为\r\n,linux平台保留不变,mac系统\n转换为\r。\r不做转换。如果以二进制方式打开文件写入字符时不做转换。以文本方式打开文件,也可以写入二进制数据,以二进制方式打开文件,也可以写入文本。写入的数据是二进制还是文本,与打开方式无关,与写入使用的函数有关。要写入二进制数据应该用write,相应的读要用read。

二进制文件的读写

二进制文件不同于文本文件,它可用于任何类型的文件(包括文本文件)。对二进制文件的读写可采用从istream类继承下来的成员函数read()和ostream类继承下来的成员函数write()。文件打开操作时使用枚举常量ios::binary,例如:

ofstream fout("binary.dat",ios::out|ios::binary);
#include <iostream>
#include <fstream>
#include <string>
#include <cassert>
using namespace std;

struct Test
{
    
    
    int a;
    int b;
};
int main()
{
    
    
    Test test = {
    
     100,200 };
    ofstream fout("binary.txt", ios::out | ios::binary);
    fout.write(reinterpret_cast<char*>(&test),sizeof(Test));
    fout.close();

    Test test2;
    ifstream fin("binary.txt", ios::in | ios::binary);
    fin.read(reinterpret_cast<char*>(&test2),sizeof(Test));
    cout << test2.a << " " << test2.b << endl;

    return 0;
}

当字符串足够长时文件读写

#include <iostream>
#include <fstream>
#include <string>
#include <cassert>
using namespace std;

struct Test
{
    
    
    int x;
    string a;
    string b;
};
int main()
{
    
    
    Test t1;
    t1.x = 100;
    t1.a = "asdfsdafasfdasdfasdffsdfsdafadsadsfdfsfdsafdasfdsadfasdsfaasfddsfsadfsdfafdsasadffads";
    t1.b = "qwerqwerqrwewqrrqewewrewrqrqewerwqrewqqwerrweqrewqreqwerqwrqewrqweqrwerweqqrwerqwerwesfadsfafdfadsasfasdafsqrw";
    ofstream fout("test4.txt", ios::out | ios::binary);
    fout.write((char*)(&t1.x),sizeof(int));
    int len;
    len = t1.a.length();
    fout.write((char*)&len, sizeof(int));
    fout.write(t1.a.data(), t1.a.length());
    len = t1.b.length();
    fout.write((char*)&len, sizeof(int));
    fout.write(t1.b.data(), t1.b.length());
    fout.close();

    ifstream fin("test4.txt", ios::in | ios::binary);
    Test t2;
    fin.read((char*)(&t2.x),sizeof(int));
    fin.read((char*)(&len), sizeof(int));
    t2.a.resize(len);
    fin.read(&t2.a[0], len);
    fin.read((char*)(&len), sizeof(int));
    t2.b.resize(len);
    fin.read(&t2.b[0], len);
    cout << t2.x << " " << t2.a << " " << t2.b << endl;

    return 0;
}

当前文件流活动指针

文件流指针用以跟踪发生I/O操作的位置。每当从流中读取或写入一个字符,当前活动指针就会向前移动。当打开方式中不含有ios::ate或ios::app选项时,则文件指针被自动移到文件的开始位置,即字节地址为0的位置。

文件的随机读写seekp和seekg

  • seekp:设置输出文件流的文件流指针位置。
  • seekg:设置输入文件流的文件流指针位置。

函数参数:pos新的文件流指针位置,off需要偏移的值,dir搜索的起始位置。

文件的随机读写tellp和tellg

  • tellp:获得输出的文件流指针的当前位置,以字节为单位。
  • tellg:获得输入的文件流指针的当前位置,以字节为单位。

函数返回值实际上是一个long类型。

#include <iostream>
#include <fstream>
#include <string>
#include <cassert>
using namespace std;

struct Test
{
    
    
    int x;
    string a;
    string b;
};
int main()
{
    
    
    ifstream fin("test5.txt");
    assert(fin);
    fin.seekg(2);

    char ch;
    fin.get(ch);
    cout << ch << endl;

    fin.seekg(-1, ios::end);
    fin.get(ch);
    cout << ch << endl;

    fin.seekg(0, ios::end);
    streampos pos = fin.tellg();
    cout << pos << endl;

    return 0;
}

seek_dir

dir参数用于对文件流指针的定位操作上,代表搜索的起始位置。在ios中定义的枚举类型:

enum seek_dir{
    
    beg,cur,end};

beg表示文件流的起始位置,cur表示文件流的当前位置,end表示文件流的结束位置。

输出流的格式化

数据输入输出的格式控制使用系统头文件中提供的操纵符。把它们作为插入操作符<<的输出对象即可。如setiosflags、setw、setfill、setprecision、hex、oct等。

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
    
    
    int n = 64;
    double d = 123.45;
    double d2 = 0.0187;

    //通过操纵子方式进行格式化输出
    cout << "====================宽度控制====================" << endl;
    cout << n << "#" << endl;
    cout << setw(10) << n << "#" << endl;                                   //默认右对齐
    cout << setw(10) << n << "#" << n << endl;                              //宽度控制不会影响下一个输出

    cout << "====================对齐控制====================" << endl;
    cout << setw(10) << setiosflags(ios::left) << n << "#" << endl;         //设置左对齐
    cout << setw(10) << n << "#" << endl;                                   //对齐控制影响下一个输出
    cout << setw(10) << setiosflags(ios::right) << n << "#" << endl;        //设置右对齐
    cout << setw(10) << resetiosflags(ios::left) << n << "#" << endl;       //取消左对齐

    cout << "====================对齐控制====================" << endl;
    cout << setw(10) << setfill('?') << n << "#" << endl;
    cout << setw(10) << n << "#" << endl;                                   //填充控制影响下一个输出
    cout << setw(10) << setfill(' ') << n << "#" << endl;

    cout << "====================精度控制====================" << endl;
    cout << setprecision(4) << d << endl;                                   //保留四位有效数字
    cout << setprecision(2) << d2 << endl;
    cout << setiosflags(ios::fixed);
    cout << setprecision(4) << d << endl;                                   //保留小数点后四位
    cout << setprecision(2) << d2 << endl;

    cout << "====================进制输出====================" << endl;
    cout << n << endl;
    cout << dec << n << endl;
    cout << oct << n << endl;
    cout << hex << n << endl;
    cout << endl;

    cout << setiosflags(ios::showbase);
    cout << dec << n << endl;
    cout << oct << n << endl;
    cout << hex << n << endl;
    cout << endl;

    cout << setbase(10) << n << endl;
    cout << setbase(8) << n << endl;
    cout << setbase(16) << n << endl;
    return 0;
}

通过调用流的成员函数控制格式,如setf、unsetf、width、fill、precision等。优点是在设置格式同时,可以返回以前的设置,便于恢复原来的设置。

#include <iostream>
using namespace std;

int main()
{
    
    
    int n = 64;
    double d = 123.45;
    double d2 = 0.0187;

    cout << "====================宽度控制====================" << endl;//宽度控制不会影响下一个输出
    cout << n << "#" << endl;

    cout.width(10);
    cout << n << "#" << endl;

    cout << n << "#" << endl;

    cout << "====================对齐控制====================" << endl;//对齐控制影响下一个输出
    cout.width(10);
    cout.setf(ios::left);
    cout << n << "#" << endl;

    cout.width(10);
    cout << n << "#" << endl;

    cout.width(10);
    cout.unsetf(ios::left);
    cout << n << "#" << endl;

    cout.width(10);
    cout.setf(ios::right);
    cout << n << "#" << endl;

    cout << "====================对齐控制====================" << endl;//填充控制影响下一个输出
    cout.width(10);
    cout.fill('?');
    cout << n << "#" << endl;

    cout.width(10);
    cout << n << "#" << endl;

    cout.width(10);
    cout.fill(' ');
    cout << n << "#" << endl;

    cout << "====================精度控制====================" << endl;
    cout.precision(4);//保留四位有效数字
    cout << d << endl;
    cout.precision(2);
    cout << d2 << endl;

    cout.setf(ios::fixed);
    cout.precision(4);//保留小数点后四位
    cout << d << endl;
    cout.precision(2);
    cout << d2 << endl;

    cout << "====================进制输出====================" << endl;
    cout.setf(ios::showbase);
    cout << n << endl;
    cout.unsetf(ios::dec);
    cout.setf(ios::oct);
    cout << n << endl;

    cout.unsetf(ios::showbase);
    cout << n << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43912621/article/details/131024872