C++:关于IO流

目录

IO类框架​编辑

IO流机制

C/C++缓冲区同步

错误状态标志 iostate

tie

输入输出

标准流对象

常见输入策略

文件IO

ifstream

get

read

ofstream

write

字符流转换

istringstream

ostringstream


IO类框架

  • ios:定义了基本的IO框架,比如IO的错误标志,其中io_base是所有IO类的基类
  • iostream:包含了标准输入输出,最常用的头文件
  • fstream:用于文件输入输出
  • sstream:用于操作字符串,可以非常方便的进行字符串格式化与解析

IO流机制

在C++中,额外增加了许多IO的特殊机制,这些机制都在ios头文件中。

C/C++缓冲区同步

C++是在C语言基础上改进的语言,要兼容C语言的绝大部分内容,自然包括C语言本身的IO机制。对于标准输入而言,C++使用cincout,而C语言使用printfscanf。它们的功能相同,挑选一个使用即可,但是免不了会有用户两个版本交替使用,此时就可能带来同步问题。

因此C++中cout没有自己的缓冲区,而是与printf共用缓冲区,这样就算混用两者,也不会造成输出错误。

但是正因为兼容了printfcout就要去做很多额外的工作,那么cout的效率就会下降,为此,C++也提供了接口sync_with_stdio用于处理C/C++之间的同步问题。

默认sync = true,也就是C/C++之间要进行同步。此时可以通过该函数手动调整为false,接触同步关系: 

sync_with_stdio(false);

此时C/C++就不兼容了,如果再混用coutprintf,就可能导致输出顺序错误。但是相应的,coutcin的效率会提高。


错误状态标志 iostate

每一个IO对象,内部都存储了字节的错误状态标志,其为四个比特位,分别是goodbiteofbitfailbitbadbit,四者含义如下:

iostate描述goodeoffailbad
goodbit没有错误1000
eopbit文件读取结束0100
failbit逻辑错误0010
badbit读写错误0011

执行如下代码:

int i;
cout << "请输入数字:";
cin >> i;

cout << "good = " << cin.good() << endl;
cout << "eof = " << cin.eof() << endl;
cout << "fail = " << cin.fail() << endl;
cout << "bad = " << cin.bad() << endl;

次数输入一个不是数字的内容,比如字符x

请输入数字:s
good = 0
eof = 0
fail = 1
bad = 0

一旦出现错误,这个cin对象就无法再使用了,必须通过clear来重置错误状态标志。

当IO对象内部的错误状态标志不正确,那么再次进行IO时就无法正常进行。clear用于将错误状态标志重置为goodbit = 1,其余位变为0,这样IO对象就可以再次使用了。

int i;
cout << "请输入数字:";
cin >> i;

cout << "good = " << cin.good() << endl;
cout << "eof = " << cin.eof() << endl;
cout << "fail = " << cin.fail() << endl;
cout << "bad = " << cin.bad() << endl;

cin.clear();
cin.get();

cout << "请输入数字:";
cin >> i;
cout << "good = " << cin.good() << endl;
cout << "eof = " << cin.eof() << endl;
cout << "fail = " << cin.fail() << endl;
cout << "bad = " << cin.bad() << endl;

这一次第一次输入字母x,在clear之后再输入数字1

clear之后紧跟了一个get,这是因为之前输入的字符x没有被读取走,发生了错误后,字符依然留在缓冲区中,如果clear后直接cin >>,那么cin还会读取到留在缓冲区中的x,导致一样的错误!所以要用get先把缓冲区中的字符读走,再让用户输入。

请输入数字:x
good = 0
eof = 0
fail = 1
bad = 0
请输入数字:1
good = 1
eof = 0
fail = 0
bad = 0

tie

在这里插入图片描述

tie是一种特殊的绑定机制,其将一个istream对象绑定到另一个ostream对象上。istream收到数据时,会刷新对应的ostream的缓冲区。 

默认情况下cin会绑定到cout上,而很多用户用完cin接收数据后,很有可能会用cout把处理好的数据输出出去。cout的缓冲区中可能存有之前没有刷新的数据,为了保证这次输出的数据不受影响,在cin读取数据时,就把cout的缓冲区提前刷新一次通过tid进行绑定的机制,其实就是将未来有可能要使用的缓冲区提前刷新,为后续数据预留空间的机制

通过函数tie可以发现,其有两个重载,对于无参的tie,其返回该对象绑定到的ostream对象的指针;对于有参的tie,传入一个ostream*指针,将该对象重新绑定到其它ostream对象上,如果传入nullptr,那么相当于解绑,不绑定任何一个ostream对象。

*cin.tie() << "hello world!" << endl;

无参的cin.tie()返回其绑定的对象指针,也就是默认的cout,在对指针解引用,就相当于cout <<此处想证明cin默认绑定到cout 。

解除cin的绑定:

cin.tie(nullptr);

由于cin自动绑定到cout,那么每次cin读取数据都会刷新cout的缓冲区,这会影响到cin的效率,所以竞赛中会进行一个这样的解绑操作,让cin读取时不再刷新cout缓冲区,提高IO效率。


输入输出

标准流对象

C++的标准输入输出定义在头文件<iostream>中,其中包含istreamostream这两个类,分别用于输入输出。其中istream定义了基本的cin对象,用于读取stdin流,ostream定义了coutcerrclog对象,其中cout输出到stdout流,cerrclog输出到stderr流。

使用上,我们常用<<>>这样的操作符来操作标准流对象,这是基于操作符重载,以ostream重载<<为例:

ostream重载了<<,常见的内置类型都已经完成了重载,完成对应类型的输出。由于函数重载后会自动推导类型,所以不用像printf一样考虑变量的类型,再使用%s%d这样的占位符 

此处operator的返回值ostream&,是为了作用于连续输出:

cout << "hello" << " world!";

这个代码本质是operator<<(operator<<(cout, "hello"), " world!") ,其中operator<<(cout, "hello")返回了cout,所以输出完hello后代码变成operator<<(cout, " world!"),所以cout可以这样进行连续输出。

如果想要输出自定义类型,也可以自己重载对应的操作符:

struct date
{
    int _year;
    int _month;
    int _day;

    date(int year, int month, int day)
        : _year(year)
        , _month(month)
        , _day(day)
    {}
};

ostream& operator<<(ostream& out, const date& d)
{
    cout << d._year << "-" << d._month << "-" << d._day;

    return out;
}

以上就是给date类重载了ostream的输出,注意函数最后要返回ostream对象的引用。


常见输入策略

在写题时,常需要进行循环输入,一般来说会有以下常用格式:

  • 单变量循环输入:
while (cin >> str)
{
	// ...
}
  • 多变量循环输入:
while (cin >> str1 >> str2 >> str3)
{
	// ...
}

while内部需要一个bool类型的值判断真假,此处不论输入几个变量都返回istream对象,为什么可以把这个输入语句直接放到while内部做判断条件? 

其实ios类重载了operator bool也就是到bool的隐式类型转化,因此istream可以隐式转化为bool类型,满足while的判断要求。 


文件IO

在C++中,读写一个文件的基本套路如下:

  1. 定义一个文件流对象
  2. 打开指定文件
  3. 进行IO
  4. 关闭文件

而流文件对象有ifstreamofstream,分别作用于输入输出。需要头文件<fstream>

ifstream

在这里插入图片描述

ifstream的构造函数如上,最常用的是第二个初始化的构造:

explicit ifstream (const char* filename, ios_base::openmode mode = ios_base::in);
  • filename:文件名
  • mode:打开模式
方式功能
in读取文件内容
out输出内容到文件
binary以二进制形式打开
ate打开后,文件指针初始指向文件末尾
app以追加的形式打开文件
trunc打开时清空文件内容
  • ate:只是初始的时候文件指针在末尾,后续可以随意移动文件指针,自由读写。
  • app:每次进行写入操作,文件指针都会强制跳转到文件末尾,也就是说只能操作文件的末尾。

以上模式在ios_base类域中,可以混用,每个模式占一个位,传参时以按位或的形式传入:

ifstream ifs ("test.cpp", ios_base::in | ios_base::binary);
#include <iostream>
#include <fstream>
#include <windows.h>

using namespace std;

int main()
{
    ifstream ifs("test.cpp", ios_base::in);

    string buf;

    while (ifs >> buf)
    {
        cout << buf;
    }
    
	ifs.close();
	
    return 0;
}

重载operator>>在实现时,会自动忽略空格与换行。对于cincout来说,这可以很方便的读取到变量,而对于文件来说,就不怎么适合了,所以一般不使用这个重载读取文件。 

get

为了控制文件读取,一般会使用istream::get方法:

在这里插入图片描述

#include <iostream>
#include <fstream>
#include <windows.h>

using namespace std;

int main()
{
    ifstream ifs("test.cpp", ios_base::in);

    char ch;

    while (ifs.get(ch))
    {
        cout << ch;
    }
    
	ifs.close();

    return 0;
}

此时按照字符读取内容,空格与换行也会被读取,最后输出的内容也就是格式的内容了。 

read

对于二进制文件来说,一般会使用istream::read读取:

在这里插入图片描述


ofstream

在这里插入图片描述

在打开方式上,ofstreamifstream差不多,都是传入一个文件名以及一个打开模式。


write

对于二进制文件写入,则使用write多:

在这里插入图片描述


字符流转换

在字符串处理方面,C++ 提供了两个非常有用的流类:istringstream 和 ostringstream。这两个类分别用于字符串到数据的解析,以及将数据转换为字符串。需要头文件<sstream>

istringstream

istringstream 是 C++ 标准库中的一个类,属于 std::istringstream。它继承自 std::istream,可以将一个字符串视为输入流,从中提取各种数据类型。

string input = "123 45.67 Hello";
istringstream iss(input);

int integer;
double floating;
string word;

// 从字符串中提取数据
iss >> integer >> floating >> word;

在这段代码中,首先创建了一个 std::istringstream 对象 iss,并将 input 作为输入流。通过重载的 >> 操作符,我们可以依次从流中提取整数、浮点数和字符串,并将它们存储到相应的变量中。

这种使用方式非常简洁,适合从格式化的字符串中提取数据。 


ostringstream

ostringstream 是 C++ 标准库中的另一个重要流类,属于 std::ostringstream。它继承自 std::ostream,主要用于将各种数据类型格式化为字符串。

int integer = 123;
double floating = 45.67;
string word = "Hello";

ostringstream oss;
oss << "Integer: " << integer << ", Double: " << floating << ", String: " << word;

string output = oss.str();
cout << output << endl;
Integer: 123, Double: 45.67, String: Hello

ostringstream可以很方便的生成格式化字符串。 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值