输入输出流操作

输入输出流操作

C++以面向对象的观点,把I/O(input/output)抽象为流类。I/O流提供了低级和高级的输入/输出功能。低级I/O是无格式的数据传输,对字节序列不作解释;高级I/O是格式化的数据传输,系统会对字节序列按照数据类型进行解释。

1.流类库

流类库有三个基类streambuf(流缓冲区)类,ios类,iostream_init类(用于流类初始化操作)。

在这里插入图片描述

istream:
1.ifstream是文件输入流,用于对文件的提取操作
2.istrstream是字符串输入流,用于对字符串的提取操作
3.istream_withassign是重载赋值运算符的输入流类,标准输入流cin属于该类对象
ostream:
1.ostream是文件输出流
2.ostrstream是字符串输出流
3.ostream_withassign是重载赋值运算符的输出流类,标准输出流cout属于该类对象

istream和ostream派生出iostream类。有三个派生类:fstream,strstream,stdiostream。

2.输入流操作

函数功能
read()istream& read (char* s, streamsize n) 读取流中n个字节并存入s中
get()从流中提取字符(包括空格)

get的不同形式

函数功能
int get()从输入流中提取一个字符,并将字符的ASCII作为返回值,遇到文件结束符返回EOF
istream& get(char& a)从指定输入流中提取一个字符,并写入到a中。e.g: char c;c= cin.get()//cin.get(c)
istream& get(char* a,int n, char d = ‘\n’)从流当前字符开始,读取n-1个字符,或遇到指定分隔符d结束,默认换行符。字符串后会添加结束符’\0’(所以是读取n-1个字符)
istream& getline(char* a,int n,char d =’\n’)从流当前字符开始,读取n-1个字符,其他同上,区别在于get()在缓冲区不会丢弃d,getline()会丢弃d。但是都不会存入到a里面。
	char c[10];
	char test;
	cin.getline(c, 10, 'e');
	cout << c <<endl;
	cin.get(test);
	cout << test << endl;

在这里插入图片描述

	char c[10];
	char test;
	cin.get(c, 10, 'e');
	cout << c <<endl;
	cin.get(test);
	cout << test << endl;

在这里插入图片描述

*cin.get()和cin.getline()的区别

这里我们看出,cin.getline()读取到’e’时,会从缓冲区拿出来,但不放进存储的数组里面;而cin.get()读取到‘e’时,不会从缓冲区拿出来,后面cin>>test,test就能拿到e了。

另外,假如在指定取10个字符(包括结束符,即从缓冲区取9个)的情况下,如果你输入了11个字符,前面用cin.get来获取缓冲区的字符,后面再cin>>test是没有问题的。结果如图:
在这里插入图片描述

但如果用cin.getline来获取缓冲区字符,后面cin>>test就不能获取了。
在这里插入图片描述
因为超过了字符,cin.getline()会将输入流的failbit设为1,后面的输入流操作就失败了,这时候如果想让后面的cin>>test拿到相应的值,就要加上cin.clear()将输入流的goodbit设为1。

	char c[10];
	char test;
	cin.getline(c, 10, 'e');
	cout << c <<endl;
	cout <<"failbit的状态:"<< cin.fail() << endl;
	cin.clear();
	cin.get(test);
	cout << test << endl;

在这里插入图片描述

函数功能
ignore提取并丢弃流中指定字符
peek返回流中的下一个字符的ASCII码
gcount统计最后输入的字符(非标准操作cin>>)个数
seekg移动输入流指针
tellg返回输入流中指定位置的指针值
	char c[10];
	char test[10];
	cin.get(c, 10, 'e');
	cout << c << endl;
	cout << cin.gcount() << endl;
	cout <<"下一个字符:"<< char(cin.peek()) << endl;
	cin.ignore(10, 'e');//忽略十个字符,将其从流中删除。
	cin.get(test,10);
	cout << test << endl;
	cout << cin.gcount() << endl;

在这里插入图片描述
read,seekg,tellg函数在进行文件处理时用到。

3.输出流操作

函数功能
put无格式,插入一个字节
write无格式插入一个字符序列
flush刷新输出流
seekp移动输出流指针
tellp返回输出流中指定位置的指针值
	char c;
	cin.get(c);
	cout.put(c);

输入a,输出a。其他函数常在进行文件处理时使用。

4.流错误状态

错误状态字描述
标识常量意义
goodbit0x00状态正常
eofbit0x01遇到文件结束符
failbit0x02i/o操作失败,数据未丢失,可恢复
badbit0x04非法操作,数据丢失不可恢复

在这里插入图片描述
ios类中相关公有成员函数

1.int good() const;
2.int eof() const;
3.int fail() const;
4.int bad()const;

上述函数可以判断对应标识常量的状态。
!cin 在good()返回1时该函数返回0,可用于判断流状态。

int rdstate() const该函数返回状态字,fail()是failbit或者badbit设置后才返回1

**void clear(int nState = 0)**将流状态设置为对应的状态字,默认参数0,对应ios::goodbit。使用时可使用cin.clear(ios::goodbit)形式。

	char c;
	cin.get(c);
	cout << cin.good() << endl;
	cout << cin.eof() << endl;
	cout << cin.fail() << endl;
	cout << cin.bad() << endl;

在这里插入图片描述

5.格式控制

标志常量意义输入输出
ios::left设置左对齐输出O
ios::right设置右对齐输出O
ios::interal在符号位或基数指示符后填入字符O
	cout.width(5);//设置宽度为5
	cout.fill('@');//设置填充字符
	cout.setf(ios::left);//设置左对齐
	cout << 5 << endl;
	cout.setf(ios::right, ios::left);//设置右对齐,取消左对齐
	cout.width(5);//重新设置宽度,否则下面是没有设置的
	cout << 5 << endl;

在这里插入图片描述

标志常量意义输入输出
ios::dec转化为十进制形式I/O
ios::oct转化为八进制形式I/O
ios::hex转化为十六进制形式I/O
ios::showbase输出中显示基数指示符(输出十六进制前+0x,八进制前+0)O
ios::uppercase输出十六进制时一律用大写字母O
ios::showpoint显示小数点O
ios::showpos正数前加“+”号O
ios::scientific科学计数法显示浮点数O
ios::fixed定点形式显示浮点数O
ios::basefield   值为dec|oct|hex
ios::adjustfield 值为left|right|internal
ios::floatfield  值为scientific|fixed
控制格式的函数:
函数功能
long setf(long flag)设置参数flag指定的标志位,返回更新前的标志字
long setf(long aflag,long bflag)将b标志位清0,将a标志位置1
long unsetf(long flag)将flag标志位清0
int width(int a)将下一个显示宽度设置为a,默认右对齐,如果a小于数据宽度,则无效。
int width() const返回当前的输出宽度
char fill(char c)当设置的数据宽度大于输出的数据宽度,填充字符c,默认空白符
char fill() const返回当前使用的填充符
int precision(int p)设置数据显示精度,如果浮点数以定点形式输出,p为小数点后的数字位数;如果以科学记数法输出,p为位数精度位数(包括小数点)
int precision() const返回当前数据的显示精度值
	int a, b, c;
	cin >> a;
	cin.setf(ios::oct,ios::basefield);//表明输入时是八进制
	cin >> b;
	cin.setf(ios::hex,ios::basefield);//表明输入时是十六进制
	cin >> c;
	cout << "十进制显示:" << endl;
	cout << "a = " << a << "\nb = " << b << "\nc =" << c << endl;
	cout << "八进制显示:" << endl;
	cout.setf(ios::oct, ios::basefield);//表明八进制输出
	cout << "a = " << a << "\nb = " << b << "\nc =" << c << endl;
	cout << "十六进制显示:" << endl;
	cout.setf(ios::hex, ios::basefield);//表明十六进制输出
	cout << "a = " << a << "\nb = " << b << "\nc =" << c << endl;
	cout << "带符号的十六进制显示:" << endl;
	cout.setf(ios::uppercase | ios::showpos | ios::showbase);
	cout << "a = " << a << "\nb = " << b << "\nc =" << c << endl;

在这里插入图片描述
设置新的显示方式前,要将与之矛盾的标志位清0,否则会出现问题。

	double x = 22.0 / 7;
	int i;
	cout << x << endl;
	cout << "output in fixed:\n";
	cout.setf(ios::fixed | ios::showpos);//定点输出,显示+号
	for (i = 1; i <= 5; i++)
	{
		cout.precision(i);
		cout <<x<< endl;
	}	
	cout << "compare with the last:" << endl;
	cout.setf(ios::scientific, ios::showpos);//未将定点输出位置0
	for (i = 1; i <= 5; i++)
	{
		cout.precision(i);
		cout << x *1e5<< endl;
	}
	cout << "output in scientific:\n";
	cout.setf(ios::scientific,ios::fixed |ios::showpos);//将定点输出位置0
	for (i = 1; i <= 5; i++)
	{
		cout.precision(i);
		cout << x * 1e5 << endl;//x乘10的五次方
	}

在这里插入图片描述

格式控制符

上面格式控制函数可能略有繁琐,C++在istream和ostream中定义了一批函数,可作为"<<“和”>>"的右操作数,实现格式控制。
1.iostream中的控制符

控制符功能输入/输出
endl输出一个换行符,并清空流O
ends输出一个字符串结束符(相当于空格)O
flush清空缓冲区O
dec用十进制输入或输出I/O
oct用八进制输入或输出I/O
hex用十六进制输入或输出I/O
int a,b,c;
cin>>dec>>a;
cin>>oct>>b;
cin>>hex>>c;//下面若不注明,cin>>d则是和最后一次使用控制符一样,十六进制
cout<<dec<<a;
cout<<oct<<b;
cout<<hex<<c;

2.iomanip中的控制符
使用前应#include<iomanip>

控制符功能输入/输出
resetiosflags(ios::flag)清除flag指定的标志位I/O
setiosflags(ios::flag)设置flag指定的标志位I/O
setbase(int b)设定基数8,10,16从而实现各种进制输入输出I/O
setfill(char c)设置填充符O
setprecision(int n)设置浮点数输出宽度O
setw(int n)设置宽度O
	int a,b;
	double c = 22.0 / 7;
	cin >> a>>b;
	cout << resetiosflags(ios::basefield) << setiosflags(ios::oct) << a << endl;
	cout << a<<' '<<b << endl;
	cout << setbase(10) << a <<' '<< b << endl;
	cout << setiosflags(ios::right) << setfill('*') << setw(5) << a << endl;
	cout << setiosflags(ios::fixed) << setprecision(5) << c<<endl;

在这里插入图片描述

6.串流

C++标准流对象是内存与外部设备的数据传输,C++还可以定义连接内存的流对象——字符串流。
1.strstream、istrstream、ostrstream是ios的派生类,支持C语言的字符串输入、输出。使用这些类要#include<strstream>。
2.stringstream、istringstream、ostringstream类支持从string对象输入/输出数据。使用这些类要#include<sstream>。

#include<iostream>
#include<string>
#include<sstream>
using namespace std;
int main()
{
	string testStr("Input test 256 * 0.5");//空格注意,避免把数据当字符串提取了
	string s1, s2, s3;
	double x, y;
	istringstream input(testStr);//建立istringstream类对象,与串对象连接
	input >> s1 >> s2 >> x >> s3 >> y;
	cout << s1 <<' '<< s2 << ' ' << x << s3 << y << '=' << x * y << endl;
}
#include<iostream>
#include<strstream>
using namespace std;
int main()
{
	const char* testStr = "Input test 256 * 0.5";
	char s1[10], s2[10], s3[10];
	double x, y;
	istrstream input(testStr);//建立istrstream对象
	input >> s1 >> s2 >> x >> s3 >> y;
	cout << s1 << ' ' << s2 << ' ' << x << s3 << y << '=' << x * y << endl;
}

在这里插入图片描述

#include<iostream>
#include<sstream>
using namespace std;
int main()
{
	ostringstream Output;//建立输出流对象
	double x, y;
	cout << "Input x :";
	cin >> x;
	cout << "Input y :";
	cin >> y;
	Output << x << "*" << y << "=" << x * y << endl;
	cout << Output.str();
}
#include<iostream>
#include<strstream>
using namespace std;
int main()
{
	char buf[80];
	ostrstream Output(buf, sizeof(buf));//建立输出流对象
	double x, y;
	cout << "Input x :";
	cin >> x;
	cout << "Input y :";
	cin >> y;
	Output << x << "*" << y << "=" << x * y << '\0';
	cout << buf<<endl;
}

在这里插入图片描述

7.文件处理

文件处理都是按文件指针来操作,进行一次读操作,写操作后,对应的指针位置都会发生变动,下一次的读/写操作就从指针的位置开始操作。

标识常量意义
ios::cur当前流指针位置
ios::beg流的开始位置
ios::end流的尾部
函数功能
seekp(long n)相当于以文件头部为参考,将指针向后移动n个字节
seekp(long n,ios::seek_dir dir)以dir为参照,将指针向后移动n个字节

seekp(n)相当于seekp(n,ios::beg),fstream类中有seekg和seekp前者为输入流指针(get),后者为输出流指针(put)。两个函数用法类似。

打开文件
打开文件:(1)流类 对象名;对象名.open(文件名,方式); (2)流类对象名(文件名,方式)
流类:ifstream(只读方式),ofstream(写方式),fstream(可读又可写)

方式

标识常量意义
ios::in读方式打开文件,文件指针在文件头。
ios::out写方式打开文件,文件不存在则创建,但不能创建目录
ios::ate打开文件时文件指针指向文件末尾,可通过移动文件指针的方式读写数据,不存在则创建文件
ios::app追加方式,将向文件中输出的内容追加到文件尾部,不存在则创建文件
ios::trunc删除文件现有的内容(ios::out的默认操作)
ios::nocreate如果文件不存在,则打开操作失败
ios::noreplace如果文件存在,则打开操作失败
ios::binary以二进制代码方式打开,默认为文本方式

另外,
ios::in|ios::out 打开文件时不会清空内容,文件指针在文件头。
ios::app|ios::out 打开文件时不会清空内容,文件指针在文件尾部,且移动指针操作无效。
ios::ate|ios::out 打开文件时清空文件内容。
ios::app|ios::in|ios::out 打开文件时不会清空文件内容,文件指针在文件尾部,且移动指针操作无效

文件名可使用绝对路径和相对路径,文件路径使用参照:
https://www.cnblogs.com/vranger/p/3820783.html

关闭文件

对象名.close() 当一个流对象的生存周期结束时,系统会自动关闭文件,但是为了将缓冲区的东西及时写入文件,应手动关闭文件。

读/写文件

文本文件写入可以用插入符,流对象名.get()等等。在想把文本文件里的字符解释为int应该用插入符。如果单纯是解释为字符的话可以用.get()形式等
二进制文件写入用流对象名.write((char *)&s,sizeof(s))。读取内容用流对象名.read((char *)&s,sizeof(s))来读取相应写入的内容。
示例:
文本文件

#include<iostream>
#include<fstream>
#include<string>
using namespace std;
class student {
public:
	string name;
	int age;
	student(string c = "", int a=0) :name(c), age(a) {}
};
int main()
{
	student s;
	int op = 1;
	ofstream output("test.txt", ios::app);//建立文件输出流对象
	while (op)
	{
		cout << "姓名:"; cin >> s.name;
		cout << "年龄:"; cin >> s.age;
		output << s.name << ' ' << s.age<<endl;//分隔符有必要,没有读取文件时会将年龄也放入string中。
		cout << "(1/0)?"; cin >> op;
	}
	output.close();
	cout << "读取文件测试" << endl;
	ifstream input("test.txt", ios::in);
	if (!input)//当文件不存在时!input返回1
		cout << "文件不存在" << endl;
	else
		while (input >> s.name >> s.age)//检测是否到达文件尾
			cout << "姓名:" << s.name << ' ' << "年龄:" << s.age << endl;//会跳过分隔符
	input.close();
}

在这里插入图片描述
二进制文件

#include<iostream>
#include<fstream>
#include<string>
using namespace std;
class student {
public:
	string name;
	int age;
	student(string c = "", int a=0) :name(c), age(a) {}
};
int main()
{
	student s;
	int op = 1;
	ofstream output("test.dat", ios::binary|ios::out);
	while (op)
	{
		cout << "姓名:"; cin >> s.name;
		cout << "年龄:"; cin >> s.age;
		output.write((char*)&s, sizeof(s));//二进制文件写入方式
		cout << "(1/0)?"; cin >> op;
	}
	s.age = 0; s.name = "";
	output.write((char*)&s, sizeof(s));//写入空记录,作读文件的结束标准
	output.close();
	cout << "读取文件测试" << endl;
	ifstream input("test.dat", ios::in|ios::binary);
	if (!input)//当文件不存在时!input返回1
		cout << "文件不存在" << endl;
	else
		do {
			input.read((char*)&s, sizeof(s));
			if(s.age != 0)
			cout << "姓名:" << s.name << ' ' << "年龄:" << s.age << endl;
		} while (s.age!=0);
	input.close();
}

在这里插入图片描述
二进制文件中假如存入的数据是double类型1.5,而你想要进行修改成2.0,此时只需要移动文件指针就可以了。对于文本文件,因为里面都是字符,想要修改会比较麻烦。

#include<iostream>
#include<fstream>
using namespace std;
int main()
{
	ofstream output("test.dat", ios::binary | ios::out);
	double a[5] = { 1.1,2.2,3.3,4.4,5.5 };
	double c;
	for (int i = 0; i < 5; i++)
		output.write((char*)&a[i], sizeof(double));
	output.close();//写文件操作
	
	fstream input("test.dat", ios::binary | ios::in|ios::out);//可读又可写方便修改
	cout << "修改数值前" << endl;
	for(int i=0;i<5;i++)
	{
		input.read((char*)&c, sizeof(double));
		cout << c << endl;
	}//输出修改前的数据

	input.seekg(0);//移动读指针在文件头
	double d = 6.6;
	for (int i = 0; i < 5; i++)
	{
		input.seekg(i*sizeof(double), ios::beg);
		input.read((char*)&c, 8);
		if (c == 3.3)
		{
			input.seekp(i*sizeof(double), ios::beg);//移动写指针的位置在修改地数值前
			input.write((char*)&d, 8);//覆写
		}
	}

	input.seekg(0); input.seekp(0);//主要讲读指针放在文件头,再次输出内容
	cout << "将3.3替换成6.6:" << endl;
	for (int i = 0; i < 5; i++)
	{
		input.read((char*)&c, sizeof(double));
		cout << c << endl;
	}
	input.close();
}

在这里插入图片描述

*如何判断文本文件是否到达结尾

对于文本文件,每个文件的最后都会有个文件结束符(内存上十六进制是0xFF,十进制为-1),我们通常使用 流对象名.eof()判断是否结束,这时候会有一些问题出现。

最后一个字符不是空格,换行符,tab

在这里插入图片描述
如上面文本文件是1空格2空格3但在a是char类型的情况下,用input.eof()作为循环判断条件会出现重复输出的情况。因为input>>a,每次会读取文件一个字符(空格会忽略),已经读取完3的时候,input.eof()还是返回0因为流没有读取到eof。继续执行循环,此时读到EOF,input>>a无效,就输出上一次的值,并且这时候才会将流的eofbit设置为1。(就是eofbit是根据上次读入的值设置)
在这里插入图片描述
在这里插入图片描述
但在a是int类型的情况下,却没有重复输出。a在文本文件中应该是要读取一个数字,可能是个位数,十位数。假如是十位数就必须读入一个字符后继续读入一个字符看是否还是数字,这样读到空格的时候才会认为这次读入结束。在上例文本中,输出2后,流会读取3,然后继续读入下一个字符,读到EOF认为本次读入结束,这时候EOF已经是被流读取了,eofbit设置为1。这样就不会重复输出。
在这里插入图片描述
在同样的文本中,a是char类型,这次输出就没有重复输出了。是因为循环语句中带有input.peek();这条语句在读取完这次内容后,去看看下一个内容,但不改变流指针位置。如第一次输出3的时候也就是第一次读取3后,input.peek();会看3后面是什么,看到EOF不止是当作这次函数的返回值,同时会将eofbit设置为1。这样就不会进入下次循环了。

另外,如果用流对象名作为循环判断标准,结果会不同,一样的文本,a是int类型,这时候还是会重复输出3。流对象作为循环判断标准,当fail()或bad()返回1时,流对象的bool型才是0。当指针读到EOF时,eofbit置1,input仍然是1。这时候再执行一次循环,指针继续读下去,发现不能读下去后,将failbit设置为1,这时才退出循环。
在这里插入图片描述
如果想让a是char类型时不会重复输出,可以用input.peek()!=EOF或者input>>a作为判断标准,这时候就不会重复输出了。
在这里插入图片描述
在这里插入图片描述

最后一个字符是空格,换行符,tab

这时候,无论a是int类型还是char类型还是会重复输出。都是因为没有读入EOF,a是int类型的时候读到3后面的空格也没有继续读取。这时候就算用peek()作为作为判断条件也是会重复输出,用input>>a就可以了。
感觉判断条件用input>>a无论哪种情况都不会重复输出。

*如何判断二进制文件是否到达结尾

二进制文件里面不是用ASCII码解释的,文件指针指向文件尾ios::end时,不一定能表示最后一个完整记录的结束,如果用eof来判断,会出现些问题。
在这里插入图片描述
通常可以加一个意味结束的标志作为文件的结束记录。如上面通过判断age是否等于0来判断文件是否结束。

......
s.age = 0; s.name = "";
output.write((char*)&s, sizeof(s));//写入空记录,作读文件的结束标准
......
do {
	input.read((char*)&s, sizeof(s));
	if(s.age != 0)
	cout << "姓名:" << s.name << ' ' << "年龄:" << s.age << endl;}while(s.age!=0)
......			
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中的输入输出重载是一种通过自定义类的方式来实现对输入输出操作符的重载。通过重载输入输出操作符,我们可以实现自定义类对象的输入输出操作。 在C++中,输入操作符(>>)和输出操作符(<<)都可以被重载。重载输入操作符可以实现从标准输入或文件中读取数据到自定义类对象中,而重载输出操作符可以实现将自定义类对象的数据输出到标准输出或文件中。 下面是一个示例代码,演示了如何重载输入输出操作符: ```cpp #include <iostream> using namespace std; class MyClass { private: int data; public: MyClass(int d) : data(d) {} // 重载输入操作符 friend istream& operator>>(istream& in, MyClass& obj) { in >> obj.data; return in; } // 重载输出操作符 friend ostream& operator<<(ostream& out, const MyClass& obj) { out << obj.data; return out; } }; int main() { MyClass obj(0); cout << "请输入一个整数:"; cin >> obj; // 使用重载的输入操作符 cout << "输入的整数为:" << obj << endl; // 使用重载的输出操作符 return 0; } ``` 在上面的示例代码中,我们定义了一个名为`MyClass`的类,其中包含一个私有成员变量`data`。通过友元函数的方式,我们分别重载了输入操作符`>>`和输出操作符`<<`。在`main`函数中,我们创建了一个`MyClass`对象`obj`,并通过重载的输入操作符从标准输入中读取一个整数到`obj`中,然后通过重载的输出操作符将`obj`的数据输出到标准输出中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值