资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

C++【IO流】-创新互联

文章目录
  • 一、C语言的输入和输出
  • 二、C++中的IO流
    • 自动类型识别
    • scanf和cin等输入,都是通过空格或者换行分隔开来的
    • 多行测试用例如何写输入
  • 三、文件流
    • ifstream读取文件
      • 读取文件中不同类型的数据
    • 二进制读写和文本读写
    • 二进制文件的写出
    • 二进制文件的读取
      • 二进制读写的坑
    • 文本的方式写和读取
    • 文件流写和读(c++)
  • 四、istringstream
    • 序列化和反序列化
    • ostringstream和istringstream
    • stringstream

杜尔伯特网站制作公司哪家好,找成都创新互联公司!从网页设计、网站建设、微信开发、APP开发、响应式网站等网站项目制作,到程序开发,运营维护。成都创新互联公司于2013年成立到现在10年的时间,我们拥有了丰富的建站经验和运维经验,来保证我们的工作的顺利进行。专注于网站建设就选成都创新互联公司
一、C语言的输入和输出

C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键盘)读取数据,并将值存放在变量中。printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)。
注意宽度输出和精度输出控制。C语言借助了相应的缓冲区来进行输入与输出。如下图所示:
在这里插入图片描述
这里的设备除了终端或者是控制台之外,还可能是磁盘文件,或者是网卡。

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

printf/scanf
fprintf/fscanf
sprintf/sscanf

int main()
{int a=0;
    printf("%d",a);
    scanf("%d",&a);
    printf("%d\n",a);
    
}
二、C++中的IO流

在这里插入图片描述
istream:流提取
ostream:流输出
因为提取和输出都要写一个类,非常麻烦,就写了一个特iostream,继承了这两个类的功能
(但是因为这里存在菱形继承,所以非常复杂)

自动类型识别
int main()
{int i=0;
    double j=2.2;
    cout<

在这里插入图片描述
这是因为库底层都对其进行了函数的重载。

istream和ostream可以更好地支撑自定义类型对象的流插入和流提取。
自定义类型可以自己重载,控制流提取和流插入的方式。
(string,日期类都可以进行流插入和流提取的重载)

scanf和cin等输入,都是通过空格或者换行分隔开来的
#includeint main()
{int year ,month ,day;
    //输入多个值默认输入是用空格或者换行分隔开来的。
    cin>>year>>month>>day;
    scanf("%d%d%d",&year,&month,&day);
    scanf("%d %d %d",&year,&month,&day);//不需要去加空格
    //如果日期是20221128这样输入的
    //我们可以用这种方式将我们的日期进行分隔
    scanf("%4d%2d%2d",&year,&month,&day);
    cout<>str;
    year=stoi(str.substr(0,4));
    month=stoi(str.substr(4,2));
    day=stoi(str.substr(6,2));
    cout<
多行测试用例如何写输入
int main()
{int year ,month ,day;
    string str;
    //使用了operate bool
    //怎么终止?ctrl+c直接kill -9终止进程。
    //ctr+z给一个流结束的标志。
    while(cin>>str)
    {year=stoi(str.substr(0,4));
        month=stoi(str.substr(4,2));
        day=stoi(str.substr(6,2));
        cout<

返回值是cin类型的对象。
一般是整型指针,比较运算符表达式道德结果可以做逻辑条件判断,0就是假,非0就是真,自定义类型一般是不能做while的条件类型判断的,这里存在隐式类型转换
想要让自定义类型能够进行转换,我们需要将其进行强制类型转换,但是一般用于类型转换的()被占用了,所以我们就用operator bool,将我们的类型转化成一个bool值,并将这个bool值进行返回。
在这里插入图片描述
这就类似于我们下面的将我们的自定义类型a赋值给内置类型int的过程。

class A
{public:
    A(int a)
        :_a(a)
    {}
private:
    int _a;
};

int main()
{//内置类型转换成自定义类型
    //隐式类型转换
    A aa1=1;//用1构造A的临时对象,再拷贝构造aa1,优化后直接构造aa1
}

如何让我们的自定义类型转换成内置类型

class A
{public:
    A(int a)
            :_a(a)
    {}
	
	//重载运算符
    operator int()
    {return _a;
    }
private:
    int _a;
};

int main()
{//自定义类型转换成内置类型1
    int i=aa1;
    cout<
三、文件流 ifstream读取文件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

int main()
{ifstream  ifs("/Users/Test.cpp");
    //读取一个字符
    char ch=ifs.get();
    while(ifs)
    {cout<

在这里插入图片描述

读取文件中不同类型的数据

这是我们test.txt中的内容
在这里插入图片描述

可以更好地兼容自定义类型的读写

class Date
{friend ostream& operator<< (ostream& out, const Date& d);
    friend istream& operator >>(istream& in, Date& d);
public:
    Date(int year = 1, int month = 1, int day = 1)
            :_year(year)
            , _month(month)
            , _day(day)
    {}
    operator bool()
    {// 这里是随意写的,假设输入_year为0,则结束
        if (_year == 0)
            return false;
        else
            return true;
    }
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 ;
    return out;
}
int main()
{ifstream  ifs("/Users/test.txt");
    //可以使用c语言的方式从文件中读取不同类型的数据
//    fscanf("%d%s%f",)
    //在c++中使用ifstream会方便一些
    int i;
    string s;
    double d;
    //对于自定义类型也是可以的
    //当然,可以的前提是日期类对象重载了流提取
    Date de;
    //流提取
    ifs>>i>>s>>d>>de;
    cout<

在这里插入图片描述

二进制读写和文本读写

二进制读写:就是文件在内存中是如何存储的,就怎样写到磁盘文件中
文本读写:序列化(json,xml),将对象数据按照某种格式进行转换,序列化成字符串,写入磁盘当中。读取回来的也是字符串,反序列化,再转换成对象数据。

二进制读写:
优点:快
缺点:写出去的内容看不见
(打开就大概率是乱码了,比方说存了一个100,但是二进制读写出来是100的补码)
文本读写:
优点:可以看见写出去的是什么
缺点:存在一个转换的过程,要慢一些

二进制文件的写出
#include#includeusing namespace std;
struct ServerInfo
{char _address[32];
    int _port;
};

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

    void WriteBin (const ServerInfo& info)
    {//默认的写都是覆盖的模式进行写入
        //但是app就是追加写,也就是append
        ofstream ofs(_filename,ios_base::out|ios_base::binary);
        //需要传入一个char*的字符串,然后第二个参数是要读取的数据的大小
        ofs.write((char*)&info,sizeof(info));
    }
private:
    string _filename;

};
int main()

{ServerInfo info={"127.0.0.1",888};
    ConfigureManager cm;
    cm.WriteBin(info);
    return 0;
}

在这里插入图片描述
这里我们的文本是不支持直接查看二进制文件的,所以我们这里的除了数字,别的我们文本的编码都是不可见的。

二进制文件的读取
#include#includeusing namespace std;
struct ServerInfo
{char _address[32];
    int _port;
};

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


    void ReadBin (const ServerInfo& info)
    {//默认的写都是覆盖的模式进行写入
        //但是app就是追加写,也就是append
        ifstream ifs(_filename,ios_base::in|ios_base::binary);
        //需要传入一个char*的字符串,然后第二个参数是要读取的数据的大小
        ifs.read((char*)&info,sizeof(info));
    }
private:
    string _filename;

};
int main()
{//二进制的读取
    ServerInfo rinfo;
    ConfigureManager cm;
    cm.ReadBin(rinfo);
    cout<

在这里插入图片描述

二进制读写的坑

我们将这里的char*的address改成string,并且将这里的address的地址改成我们博客的连接

#include#includeusing namespace std;
struct ServerInfo
{//char _address[32];
    string _address;
    int _port;
};

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

    void WriteBin (const ServerInfo& info)
    {//默认的写都是覆盖的模式进行写入
        //但是app就是追加写,也就是append
        ofstream ofs(_filename,ios_base::out|ios_base::binary);
        //需要传入一个char*的字符串,然后第二个参数是要读取的数据的大小
        ofs.write((char*)&info,sizeof(info));
    }

    void ReadBin (const ServerInfo& info)
    {//默认的写都是覆盖的模式进行写入
        //但是app就是追加写,也就是append
        ifstream ifs(_filename,ios_base::in|ios_base::binary);
        //需要传入一个char*的字符串,然后第二个参数是要读取的数据的大小
        ifs.read((char*)&info,sizeof(info));
    }
private:
    string _filename;

};
int main()
{//二进制写出去
//    ServerInfo info={"https://editor.csdn.net/md/?articleId=128086656",888};
//    ConfigureManager cm;
//    cm.WriteBin(info);
    //二进制的读取
    ServerInfo rinfo;
    ConfigureManager cm;
    cm.ReadBin(rinfo);
    cout<

我们先将数据写出去,然后重新读取进来。
(如果我们的address比较短的话,是不会报下列的错误的,但是如果address比较长的话,就会报下面的错误)
在这里插入图片描述

在windows下的string,短的时候存的就·直接用共这里的_buff[16]将其进行存储,如果这里的字符串太长了,就用string中的_ptr指向这个string的地址。
_buff[16]
_ptr
_size
_capacity
_port

ServerInfo info={"https://editor.csdn.net/md/?articleId=128086656",888};

字符串是存在我们的堆上面的,我们将我们的字符串写出去,也就是调用write写出去的时候,调用的是这个字符串的地址。这里我们的是分两次运行的,也就是我们写二进制文件的时候,这个字符串的地址。当我们的读的进程结束之后,我们再运行读取的进程,我们将这个指针读取回来的时候,这个指针已经不是我们刚刚那个string的指针了!它已经是一个野指针了!!

所以二进制读写的时候,一定要用数组,char[],不要使用string!!

文本的方式写和读取
#include#includeusing namespace std;
struct ServerInfo
{string _address;
    int _port;
};

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

    void WriteText(const ServerInfo& info)
	{//这里我们将指定的文件进行写的操作
		ofstream ofs(_filename, ios_base::out);
		//我们需要传入一个写入的子串和这个字符串的大小
		ofs.write(info._address.c_str(), info._address.size());
		//然后我们可以用换行将我们的字符串和后面的内容进行分隔
		//当然这里传入一个换行符的话,我们可以使用上面的write,或者是下面的这种简单形式的put
		ofs.put('\n');
		//将我们的_port,(原本是整型)转换成字符串的类型
		const string str = to_string(info._port);
		//然后将我们的_port的字符串输出,第一个人参数是字符串,第二个参数是字符串的大小
		ofs.write(str.c_str(), str.size());
	}

	void ReadText(ServerInfo& info)
	{//读取指定的文件
		ifstream ifs(_filename, ios_base::in);
		//创建一个字符串缓冲区
		char buff[128];
		//读取一行,放入我们上面刚刚创建的buffer当中
		ifs.getline(buff, 128);
		//然后将我们的address中的内容赋值成buff
		info._address = buff;
		//然后我们再读取进来一行数据
		ifs.getline(buff, 128);
		//然后将我们的_port,因为它原本是整型,我们需要将其重新转换成整型然后赋值给_port
		info._port = stoi(buff);
	}
private:
    string _filename;

};
int main()
{//文本方式写出去
    ServerInfo info={"127.0.0.1",888};
    ConfigureManager cm;
    cm.WriteText(info);
    //文本方式读进来
    ServerInfo rinfo;
//    ConfigureManager cm;
    cm.ReadText(rinfo);
    cout<

可以成功将文本写出,并且正常读取
在这里插入图片描述
可以读取到我们的文本中的内容

在这里插入图片描述

但是这样的的方式非常麻烦,我们需要在写入的时候手动成员的每一个参数转换成字符串类型,再读取的时候,还要将读取到的字符串转换成对应的类型放入我们的成员中。
所以我们可以用C++给我们提供的文件流的读和写来帮助我们进行上述操作。

文件流写和读(c++)
#include#includeusing namespace std;


class Date
{//如果是自定义类的话,我们需要重载流插入和流提取
    friend ostream& operator<< (ostream& out, const Date& d);
    friend istream& operator >>(istream& in, Date& d);
public:
    Date(int year = 1, int month = 1, int day = 1)
            :_year(year)
            , _month(month)
            , _day(day)
    {}
    operator bool()
    {// 这里是随意写的,假设输入_year为0,则结束
        if (_year == 0)
            return false;
        else
            return true;
    }
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 ;
    return out;
}
struct ServerInfo
{string _address;
    int _port;
    Date date;
};

struct ConfigureManager
{public:
    ConfigureManager(const char* filename="./server.config")
        :_filename(filename)
    {}
    void WriteText(const ServerInfo& info){ofstream  ofs(_filename,ios_base::out);
        //这里我们流插入一个成员,我们最好加一个换行或者空格
        //这样可以防止我们的流提取的时候,读取到的数据粘连在一起
        ofs<ifstream ifs(_filename,ios_base::in|ios_base::binary);
        ifs>>info._address>>info._port>>info.date;
    }
private:
    string _filename;

};
int main()
{//二进制写出去
//    ServerInfo info={"127.0.0.1",888};
    ServerInfo info={"127.0.0.1",888,{2022,11,30}};
    ConfigureManager cm;
    cm.WriteText(info);
    //二进制的读取
    ServerInfo rinfo;
//    ConfigureManager cm;
    cm.ReadText(rinfo);
    cout<

文本方式读写的文件是可以直接进行查看的

在这里插入图片描述

然后我们也是可以正常读取到的

在这里插入图片描述

四、istringstream 序列化和反序列化 ostringstream和istringstream
#includeusing namespace std;
class Date
{friend ostream& operator<< (ostream& out, const Date& d);
    friend istream& operator >>(istream& in, Date& d);
public:
    Date(int year = 1, int month = 1, int day = 1)
            :_year(year)
            , _month(month)
            , _day(day)
    {}
    operator bool()
    {// 这里是随意写的,假设输入_year为0,则结束
        if (_year == 0)
            return false;
        else
            return true;
    }
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 ;
    return out;
}
struct ChatInfo
{string _name; // 名字
    int _id; // id
    Date _date; // 时间
    string _msg; // 聊天信息
};

int main()
{//序列化
    ChatInfo winfo={"催逝员",114514,{2022,11,30},"啊哈哈哈哈,鸡汤来喽,哈哈哈"};
    //对应的是sprintf
    ostringstream oss;
    oss<>rInfo._name;
    iss>>rInfo._id;
    iss>>rInfo._date;
    iss>>rInfo._msg;

    cout<<"------------------------------------------"<

在这里插入图片描述

stringstream

在库中stringstream继承了istringstream和ostringstream的功能,所以直接调用这个stringstream就可以了

我们可以将我们上面的代码修改如下

#includeusing namespace std;
class Date
{friend ostream& operator<< (ostream& out, const Date& d);
    friend istream& operator >>(istream& in, Date& d);
public:
    Date(int year = 1, int month = 1, int day = 1)
            :_year(year)
            , _month(month)
            , _day(day)
    {}
    operator bool()
    {// 这里是随意写的,假设输入_year为0,则结束
        if (_year == 0)
            return false;
        else
            return true;
    }
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 ;
    return out;
}
struct ChatInfo
{string _name; // 名字
    int _id; // id
    Date _date; // 时间
    string _msg; // 聊天信息
};

int main()
{//序列化
    ChatInfo winfo={"催逝员",114514,{2022,11,30},"啊哈哈哈哈,鸡汤来喽,哈哈哈"};
    //对应的是sprintf
    stringstream oss;
    oss<>rInfo._name;
    iss>>rInfo._id;
    iss>>rInfo._date;
    iss>>rInfo._msg;

    cout<<"------------------------------------------"<

在这里插入图片描述

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


当前名称:C++【IO流】-创新互联
网址分享:http://cdkjz.cn/article/deshos.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

业务热线:400-028-6601 / 大客户专线   成都:13518219792   座机:028-86922220