成都网站建设设计

将想法与焦点和您一起共享

【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、clog是ostream类的三个不同的对象,因此这三个对象现在基本没有区别,只是应用场景不同。

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

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


分享标题:【C++】IO流-创新互联
文章路径:http://chengdu.cdxwcx.cn/article/ddjios.html