【C++】IO流

目录

一、C语言IO

二、C++IO

1.标准IO

2.文件IO

3、字符串IO

总结


一、C语言IO

C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()scanf(): 从标准输入设备(键 盘)读取数据,并将值存放在变量中printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)。

输入输出缓冲区的理解:
1.可以屏蔽掉低级I/O的实现,低级I/O的实现依赖操作系统本身内核的实现,所以如果能够屏蔽这部分的差异,可以很容易写出可移植的程序
2.可以使用这部分的内容实现“行”读取的行为,对于计算机而言是没有“行”这个概念,有了这部分,就可以定义“行”的概念,然后解析缓冲区的内容,返回一个“行”。

二、C++IO

1.标准IO

C++IO具有一个庞大的体系

我们发现C++IO体系十分的复杂,甚至出现了菱形继承,这种十分不推荐的继承 

istream,ostream继承了ios,iostream又继承了istream,ostream

C++编译之后,在操作系统中运行,变成了一个进程,操作系统会默认打开三个流,标准输入,标准输出,标准错误,在C++中对应为cin, cout, cerr

从上图可以看出,cout、cerr、clogostream类的三个不同的对象,因此这三个对象现在基本没有区别,只是应用场景不同。

cin是默认以空格或者换行符为间隔的,它主要是使用>>(流提取操作符)

同时我们也可以自己实现>>操作符的重载,使其能够与内置类型一样可以直接通过cin >> 对象

标准IO经常使用就不过多废话,主要说明下面的一个问题

void test_standIO()
{
    int n = 0;
    while(cin >> n)
    {
        cout << n << endl;
    }
}

 我们先简单的看一下这个代码,它是循环的输入到n中,并且不断地打印n

那么如何停止呢?

方法一,ctrl + c

原理是给进程发信号,让操作系统结束掉进程,同时刷新缓冲区,使最后的数据打印出来

方法二,ctrl + z + enter

原理:ctrl + z 是流结束的标志

这个代码的原理是什么?

我们看到operator>>()返回的是istream对象的引用,它是一个对象,C++对象是不能进行布尔比较的,这到底是什么情况?

原因是,它发生了隐式类型转换

他隐式类型转换为bool类型,这样就可以进行比较了

operator bool 库中实现的比较怪异,他并没有返回值

 

2.文件IO

  C++根据文件内容的数据格式分为二进制文件和文本文件。采用文件流对象操作文件的一般步骤:
1. 定义一个文件流对象
ifstream ififile(只输入用)
ofstream ofifile(只输出用)
fstream iofifile(既输入又输出用)
2. 使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系
3. 使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写
4. 关闭文件  

我们先来说明二进制文件读写

二进制读写的内容在内存中是如何存储的,它就如何写到磁盘文件中

优点:快

缺点:因为存储的都是二进制,写出去的内容一般不可见,字符串因为有编码,可以在磁盘文件中直接看到


struct ServerInof
{
    char buffer[128];
    int _port;
};

struct ConfigManager
{
    ConfigManager(const char* filename = "server.config")
        :_filename(filename)
    {}

    void WriteBin(const ServerInof& info)
    {
        ofstream ofs(_filename, ios_base::out | ios_base::binary);
        ofs.write((char*)&info, sizeof info);
    }

    void ReadBin(ServerInof& info)
    {
        ifstream ifs(_filename, ios_base::in | ios_base::binary);
        ifs.read((char*)&info, sizeof info);
    }
    
private:
    string _filename; // 配置文件
};

void test_fileIO()
{
    ServerInof info;
    // string msg("Hello World");
    // strcpy(info.buffer, msg.c_str());
    // info._port = 888;

    ConfigManager cfm;
    //cfm.WriteBin(info);
    cfm.ReadBin(info);
    string str = info.buffer;

    cout << info.buffer << endl;
    cout << info._port << endl;
}

我们先打开文件,然后将数据以二进制方式保存到文件

然后我们打开文件

内部就是一些二进制数据,我们无法直接识别

然后从文件中读取

结果是正确的

 

不过二进制读写是有十分大的局限性的,它并不适用于有深拷贝对象的场景

二进制方式读写,它直接存储的是地址,可能会有对齐等等非有效数据

既然它存储的是地址,那么在深拷贝的情况下可能会有野指针的问题

我们将上面的ServerInof中的字符数组,改成string,再次运行

然后读取数据

 

直接报了一个段错误,出现了野指针问题

所以二进制方式并不适合具有深拷贝的类

 

文本方式读写文件

文本读写:对象数据序列化字符串写出来,读回来也是字符串,反序列化转成对象数据


优点:可以看见写出去是什么

缺点:存在一个转换过程,要慢一些


struct ConfigManager
{
    ConfigManager(const char* filename = "server.config")
        :_filename(filename)
    {}

    void WriteBin(const ServerInof& info)
    {
        ofstream ofs(_filename, ios_base::out | ios_base::binary);
        ofs.write((char*)&info, sizeof info);
    }

    void ReadBin(ServerInof& info)
    {
        ifstream ifs(_filename, ios_base::in | ios_base::binary);
        ifs.read((char*)&info, sizeof info);
    }

    void WriteText(const ServerInof& info)
    {
        ofstream ofs(_filename, ios_base::out);
        //一个对象一个对象的读
        ofs.write(info.buffer.c_str(), info.buffer.size());
        ofs.put('\n');
        string str = to_string(info._port);
        ofs.write(str.c_str(), str.size());
    }

    void ReadText(ServerInof& info)
    {
        ifstream ifs(_filename, ios_base::in);
        char buffer[128];
        ifs.getline(buffer, sizeof buffer);
        info.buffer = buffer;
        ifs.getline(buffer, sizeof buffer);
        info._port = stoi(buffer);
    }

private:
    string _filename; // 配置文件
};

void test_fileIO2()
{
    ServerInof info;
    //info.buffer = "Hello World";
    //info._port = 888;
    ConfigManager cfm;
    //cfm.WriteText(info);
     cfm.ReadText(info);

     cout << info.buffer << endl;
     cout << info._port << endl;
}

 

上面的操作十分的不方便,我们可以采用与标准IO一样,cin,cout如何使用,文件IO流就如何使用

我们可以简化上面的代码

    void WriteText(const ServerInof& info)
    {
        ofstream ofs(_filename, ios_base::out);
        //一个对象一个对象的读
        // ofs.write(info.buffer.c_str(), info.buffer.size());
        // ofs.put('\n');
        // string str = to_string(info._port);
        // ofs.write(str.c_str(), str.size());
        ofs << info.buffer << endl;
        ofs << info._port << endl;
    }

    void ReadText(ServerInof& info)
    {
        ifstream ifs(_filename, ios_base::in);
        // char buffer[128];
        // ifs.getline(buffer, sizeof buffer);
        // info.buffer = buffer;
        // ifs.getline(buffer, sizeof buffer);
        // info._port = stoi(buffer);
        ifs >> info.buffer >> info._port;
    }

3、字符串IO

  在C语言中,如果想要将一个整形变量的数据转化为字符串格式,如何去做?
1. 使用itoa()函数
2. 使用sprintf()函数
但是两个函数在转化时,都得需要先给出保存结果的空间,那空间要给多大呢,就不太好界定,而且转化格式不匹配时,可能还会得到错误的结果甚至程序崩溃。  

我们以下面的简单代码讲解


class Date
{
    friend istream& operator>>(istream& in, Date& d);
    friend ostream& operator<<(ostream& out, const Date& d);
public:
    Date(int year = 1970, int month = 1, int day = 1)
        :_year(year)
        ,_month(month)
        ,_day(day)
    {}


private:
    int _year;
    int _month;
    int _day;
};

istream& operator>>(istream& in, Date& d)
{
    in >> d._year >> d._month >> d._day;
    return in;
}

ostream& operator<<(ostream& out, const Date& d)
{
    out << d._year << " 年 " << d._month << " 月 " << d._day << "日";
}

struct ChatInfo
{
    string _name;
    int _id;
    Date _date;
    string _msg;
};

void test_stringIO()
{
    //发送数据
    ChatInfo ch = {"张三", 1294268442, {2022, 11, 30}, "一起去看电影"};
    stringstream oss;
    oss << ch._date << endl;
    oss << ch._name << endl;
    oss << ch._id << endl;
    oss << ch._msg << endl;
    string str = oss.str();
    cout << str << endl;

    //解析数据
    stringstream iss(str);
    ChatInfo rinfo;
    iss >> rinfo._date;
    iss >> rinfo._name;
    iss >> rinfo._id;
    iss >> rinfo._msg;

    cout <<"-----------------------------" << endl;
    cout << rinfo._date << endl;
    cout << rinfo._name << rinfo._id << endl;
    cout << rinfo._msg << endl;
    cout <<"-----------------------------" << endl;
}

这里走的还是C++ 流提取和流插入的操作与标准IO,文件IO的操作一模一样,并且它们的接口都类似


总结


以上就是今天要讲的内容,本文仅仅简单介绍了C++IO

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值