cin和cout充当了scanf和printf的功能,但是他们不是函数,而是类对象,那么我们有必要了解一下,他们是哪些类的对象。
关于输入输出流有下图这么一个整套的东西,这整套东西很复杂,我们一般来说不去研究,但是呢,我们可以从继承关系看出它属于哪个流类的对象,首先有一个叫ios_base,ios继承ios_base,这个箭头画的不对,真正的继承体系中,箭头应该指向父类。ios又派生了istream和ostream,然后cin又属于istream,cout、cerr、clog又属于ostream,然后istream和ostream又多继承出来一个iostream。这个图很复杂,我们看下面一个
所以我们用cin和cout要包含iostream,然后这里有一个ifstream,ofstream,还有一个fstream,我们今天先学istream,和ostream,然后再学其他的。
流类综述:
1.对象不可复制或赋值
什么意思呢
#include<iostream>
#include<fstream>
using namespace std;
//我们前面写过一个这个,这里为什么要传入引用呢,并且你除了传引用之外没有别的当时可以传,除非传指针。为什么呢,我们举个例子,我们fstream fs;一下,你编译一下是没有问题的,那你在fstream fs2(fs),这是拷贝构造把。然后你就会发现编译都编译不过去了,你可以发现他会报错,新版的是把这个函数给delete掉了,老版本的会说这个函数是私有的。所以这个东西是不可用的,赋值是一样的情况。所以说流类的赋值和拷贝都是不能用的。所以传递的时候只能传递指针或者引用。
//friend ostream & operator<<(ostream &)
int main()
{
#if 0
fstream fs;
fstream fs2(fs);
#endif
fstream fs;
fstream fs2;
fs2=fs;
return 0;
}
2.流类对象是有缓冲的。
#include<iostream>
using namespace std;
int main()
{
//你随便写入一点东西,他不会一下子就写到屏幕上去的,它首先要写到缓冲里面去,等缓冲区满了再刷到屏幕上去。屏幕其实是个文件,你这样他就不会输出到屏幕上。
#if 0
cout<<"sadfaewfrwea";
while(1);
#endif
//刷缓冲有如下几种方式:
//1.就是加endl;
//2.就是flush,这里有一点,他不会引入回车(换行)。这里的目的本来也就是不引入回车
//3.unitbuf
//cout<<"sdfasdfsaf"<<flush;
cout<<unitbuf<<"sdfasdfsfsa";
}
#include<iostream>
#include<fstream>
using namespace std;
int main()
{
#if 0
int a;
//cin 背后是键盘
//cout 背后是屏幕
//cin>>a;
//cout<<a<<endl;
//fs背后是abc.txt 这里就重载了,重载了cin cout 和fs,
fstream fs("abc.txt",ios::in|ios::out|ios::trunc);
//放进去的时候有一点,放一个标志位,这里会输出123
fs<<1<<" "<<2<<" "<<3<<endl;
fs.seekp(0,ios::beg);
int x,y,z;
fs>>x>>y>>z;
cout<<x<<y<<z<<endl;
#endif
//这里输入一个a,会得到一个a,
//你在这里用cin>>val和cout>>val是一样的,只不过这里是个函数
char val;
val=cin.get();
cout.put(val);
}
#include<iostream>
#include<iomanip>
#include<fstream>
#include<string.h>
using namespace std;
/*
*cout:输出基本类型数据时,不必考虑数据是什么类型,系统会自动判断
* 选择相应的重载函数;输出用户自己定义的类型数据时,要重载<<
* 运算符。
*cerr:是在屏幕上显示出错信息,与cout用法类似,不同的是只能在屏幕上,而不能
* 在磁盘文件上输出错误信息。
*clog:用法与cerr类似,不同点是它带有缓冲区。
*这里用会cout其他的都一样的。
*/
int main()
{
#if 0
//输出16进制
int a=0x12345678;
//关闭10进制
cout.unsetf(ios::dec);
//打开16进制
cout.setf(ios::hex);
cout<<a<<endl;
//你如果又想输出10进制了,你就要把16进制给关了,然后把10进制给打开
cout.unsetf(ios::hex);
cout.setf(ios::dec);
//八进制是ios::oct
cout<<a<<endl;
#endif
#if 0
但是这个太麻烦了,这个系统函数格式化并不好用,所以就引入了流算子
要用<iomanip>这个头文件,
int x=0x123456;
//输出16进制
cout<<hex<<x<<endl;
//输出10进制
cout<<dec<<x<<endl;
//输出八进制
cout<<oct<<x<<endl;
#endif
#if 0
//强制显示小数点和尾和数符 setiosflags(ios::showpoint)
//强制显示符号:setiosflags(ios::showpos)
double d1=10/5,d2=22.0/7;
cout<<"显示小数点、尾和数符: "<<endl;
//这里输出2,2的话默认是不显示小数
cout<<d1<<endl;
//这里输出2.000,加个shwopoint就显示小数了。显示五位小数
cout<<setiosflags(ios::showpoint)<<d1<<endl;
//这里输出+3.14286 pos是正负的意思。
cout<<setiosflags(ios::showpos)<<d2<<endl;
#endif
#if 0
//设置精度(有效数字个数)
//setpreciion(n)自动四舍五入
double ddd=123.4567;
cout<<setprecision(2)<<ddd<<endl;
cout<<setprecision(3)<<ddd<<endl;
cout<<setprecision(4)<<ddd<<endl;
cout<<setprecision(5)<<ddd<<endl;
#endif
#if 0
//设置浮点数的输出是以科学记数法还是定点数
//setiosflags(ios::scientific)(科学技术法),此时精度域表示小数位数
//setiosflags(ios::fixed)(定点数),此时精度域表示小数位数
//setprecision(2)<<setiosflags(ios::fixed)设置小数的精度
double dd=123.4567;
//这里是用科学技术法
cout<<setiosflags(ios::scientific)<<dd<<endl;
//因为上面用了科学技术法,不想用了,所以这里就去掉
cout<<resetiosflags(ios::scientific);
//6位小数
cout<<setiosflags(ios::fixed)<<dd<<endl;
//4舍5入。两位小数
cout<<setprecision(2)<<setiosflags(ios::fixed)<<dd<<endl;
#endif
#if 0
uppercase:小写变大写
在16进制中大写和小写,比如说
int a=0xabcd;
int a=0xABCD是一样的
但是有的时候显示不一样
如下
int num=0xabcd;这里是小写的。
#endif
#if 0
int num=0xabcd;
//正常输出如下: 这里就是输出abcd
cout<<hex<<num<<endl;
//以大写方式输出 这里输出ABCD
cout<<setiosflags(ios::uppercase)<<hex<<num<<endl;
//你上面大写方式输出,要恢复小写输出用下面这个
cout<<resetiosflags(ios::uppercase)<<hex<<num<<endl;
#endif
//格式化输出就讲这么多
#if 0
//下面是成员函数
//成员函数
1.ostream put(char)
功能:输出一个 字符
#endif
#if 0
char str[]="Programing with C++";
cout<<sizeof(str)<<endl;
for(int i=sizeof(str)/sizeof(str[0])-2;i>=0;i--)
cout.put(*(str+i));
cout.put('\n');
#endif
#if 0
在来看看cin
#endif
#if 0
int a;
double b;
char buf[1024];
//你在这里输出如下
//12
//34.5
//ad wer
//会输出12 34.5 ad;
//最后这个遇到空格就结束了。
//这是这个东西的一个弊端
//你比如说我现在就想读一整行,怎么办呢
//不方便的事情就由成员函数来弥补
cin>>a>>b>>buf;
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"buf ="<<buf<<endl;
#endif
//我们再来看一个
//这里a读出来是123,b读出来是0 c读出来是0,这里就说明你123写进去,它就是123
//然后有个\n,它第一次读是123,然后遇到\n,遇到结束标记了,你再往后就没有了,所以我们上次就是fs<<a>>" "<<b<<" "<<c<<endl;
#if 0
fstream fs("abc.txt",ios::in|ios::out|ios::trunc);
fs<<1<<2<<3<<endl;
fs.seekg(0,ios::beg);
int a,b,c;
fs>>a>>b>>c;
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
#endif
#if 0
为了弥补这个不足,就提供了以下的不可或缺的成员函数
char get()
读入一个字符并返回(包括回车 tab 空格等空白字符)
char ch;
while((ch=cin.get())!=EOF)
{
cout.put(ch);
}
#endif
#if 0
istream & get(char &)
功能:读入一个字符,如果读取成功则返回非0值(真),如失败(遇到文件结束符),则函数返回0值(假)
char ch;
//这是个逗号表达式,逗号表达式的结果以后面那个为准
//这里就是说不是EOF就打印
while((cin.get(ch)),!cin.eof())
{
cout.put(ch);
}
return 0;
#endif
#if 0
istream &get(char *,int,char)
格式:istream &get(字符数组,字符个数n,终止字符)
或者istream &cin.get(字符指针,字符个数,终止字符)
功能:
从输入流中读取n-1字符,赋给字符数组或者字符指针指向的数组,如果在读取n-1个字符之前遇到终止字符,则提前结束。如果成功则返回非0,失败,则返回0,会清空char *指向的空间,未读到n-1个字符或终止符,则会阻塞。不能越过终止符号
#endif
//你这里输入1234i5678i,只能读到1234i,本来是可以读到5678i的,但是它跨不过终止符号
#if 0
char buf[1024];
cin.get(buf,1024,'i');
cout<<buf<<"xxx"<<endl;
//想过去就cin.ignore(1),忽略一个就可以了,这里就忽略了i,你这里
//输入1234i5678i就会输出1234xxx 5678ooo
cin.ignore(1);
memset(buf,0,1024);
cin.get(buf,1024,'i');
cout<<buf<<"ooo"<<endl;
#endif
#if 0
cin.getline(字符数组或字符指针,字符个数n[,终止字符])
与带三个参数的get()功能类似,从输入流中读取n-1字符,赋给字符数组或字符指针所指向的空间。如果在读取n-1个字符之前遇到终止字符(如果不写,默认为'\n'),则提前结束。失败会返回0情况char *,未读到n-1个字符或终止符。则会阻塞。能越过终止符号
#endif
//这里你输入1234i5678i会输出1234 5678
#if 0
char buf[1024];
cin.getline(buf,1024,'i');
cout<<buf<<endl;
cin.getline(buf,1024,'i');
cout<<buf<<endl;
#endif
#if 0
我们前面有提到ignore,可以忽略字符
当我们有 I like C/ I like C++/的时候,中间空了巨多字符
我这里其实是想打印I like C 紧接着打印I like C++
所以可以用ignore(100,i) 这里的意思是忽略100个,但是中间有i的话,到i为止
但是要注意的是他会把i也给忽略掉。那怎么办呢,我们可以用putback给推回去。
#endif
#if 0
char buf[1024];
cin.getline(buf,1024,'/');
cout<<buf<<endl;
cin.ignore(100,'I');
//这里其实就是回推一个字节,然后往那个地址写东西,比如你这里就是在原来被忽略I 的地方写个I,也可以写其他的。
cin.putback('I');
cin.getline(buf,1024,'/');
cout<<buf<<endl;
#endif
#if 0
peek 中文意思窥探的意思,这个怎么用的,这个表示我去看一眼,但是我不去取里面的数据。一旦你去取或者忽略都会涉及内部指针的移动,这个就是取看一看不动手
不去动它的指针。
#endif
char buf[1024];
cin.getline(buf,1024,'/');
cout<<buf<<endl;
cin.ignore(100,'I');
cin.putback('I');
//这里会输出I,就是你回推的那个I 暂时只发现这个函数是这么玩的
char peek=cin.peek();
cout<<peek<<endl;
cout<<buf<<endl;
}
#include<fstream>
#include<iostream>
using namespace std;
#if 0
iftream 定义一个文件输入对象
ostream 定义一个文件输出流对象
fstream 定义一个文件输入/输出流对象
#endif
int main()
{
#if 0
//和文件挂钩的方式有两种方式,第一种通过构造函数
//譬如 ifstream ifs("abc.txt");这个就表示输入用的。
//我们说的输入输出通常是不是对文件来说的,是对程序来说的
//你比如说ifstream做输入是指的它可以往我们程序里面输入数据,输出是指我们
//程序可以往外输出输出到文件上去。怎么判断对象和文件关联成功呢,以前我们打开文件要判断成不成功,在这个地方也有,就是下面的函数
ifstream ifs("abc.txt");
//这里他既然敢用非的话说明:首先非是个运算符,你这个对象跟运算符单独放在一起说明它这个地方重载了运算符。这里会输出open error,因为你没有这个文件,如果你有这个文件就不会输出 error,读是可以读出来的,但是写就不能写了,你就要用另外一个ofstream;
if(!ifs)
{
cout<<"open error"<<endl;
return 0;
}
int a,b,c;
ifs>>a>>b>>c;
cout<<a<<b<<c;
return 0;
#endif
#if 0
这里一开始是没有xxx.txt的,但是它会新建一个xxx.txt,然后你往里面写入东西,比如我这里写入1234它都写进去了。当然,你这里也不能输入。
ofstream ofs("xxx.txt");
if(!ofs)
{
cout<<"open file error";
return 0;
}
ofs<<12<<34<<endl;
return 0;
#endif
#if 0
下面这个是我们用的最多的,就是我们自己可以定制化的东西,叫做fstream
你可以通过fstream fs("xxxxx"),也可以fstream fs;fs.open("xxx");来操作,但是这里open的返回类型是void类型,所以你不能拿open的返回值取做判断。
#endif
#if 0
//这里会输出open error,也就是说这个地方没有这个文件,也就是说in和out它不负责产生文件。你加个ios::trunc,它没有的时候就会创建文件,但是这里有个特点,你有这个文件的话,你加这个trunc就会清空。如果你不想清空就用ios::app。这个表示没有的时候创建,有的话不会清空,且会追加到文件的末尾。
//大家还记不记得EOF,EOF有个特点,它读到最后一个字之后再读一次,你假设文件中有三个字符,当你指针在第一个位置的时候,它有一个标志位,假设我们有一个标志位叫flags,我们假设一下,它打开文件的时候如果成功了它的标志位就是true,下一次你在这个位置读一次,然后你发现没有这个标志位,你就读了往后移动,到第二个位置,它就把第二个位置置为true,所以到了EOF的时候,他会读一次,然后在判断,所以我们要等他操作完了之后在判断。
fstream fs;
fs.open("fs.txt",ios::in|ios::out);
if(!fs)
{
cout<<"open error"<<endl;
}
#endif
#if 0
fstream fs;
fs.open("helloworld.txt",ios::in|ios::out|ios::app);
if(!fs)
{
cout<<"open error"<<endl;
}
fs<<123<<456<<endl;
//这里是刷缓冲,把缓冲上的全部刷到相应的地方去。
fs.close();
#endif
#if 0
流文件的状态与判断
系统为了标识当前文件操作的状态,提供了标识位和检查标识位的函数
流文件的状态和判断有如下
eof():如果读文件到达文件末尾,返回true
bad():如果在读写过程中出错,返回true。例如:当我们要对一个不是打开为写状态的文件进行写入时,或者我们要写入的设备没有剩余空间的时候。
fail():除了与bad同样的情况会返回true以外,加上格式错误时也返回true,例如当想要读入一个整数,而获得一个字母的时候,也是遇到eof
good()
这个是最通用的:如果调用以上任何一个函数返回true的话,这个函数返回false
clear()
标识位一但被置位,这些标识将不会被改变,要想重置以上成员函数所检查的状态标志,你可以使用clear(),没有参数。
#endif
#if 0
//一行一行的读一个文件
fstream fs_in,fs_out;
char s_buf[1024];
fs_in.open("20180527_read.txt",ios::in|ios::out);
fs_out.open("20180527_write",ios::in|ios::out|ios::trunc);
while(fs_in.getline(s_buf,1024,'\n'),!fs_in.eof())
{
cout<<s_buf<<endl;
fs_out<<s_buf<<endl;
}
fs_in.close();
fs_out.close();
#endif
#if 0
#endif
//读写一个二进制文件
//ostream &write(const char *buf,int len);
//istream &read(const char *buf,int len)
struct Stu
{
char name[30];
char sex;
int age;
};
fstream fs_out;
fs_out.open("20180527_bin",ios::in|ios::out|ios::trunc|ios::binary);
if(!fs_out)
{
cout<<"open error"<<endl;
}
Stu s[5]=
{
{"zhaosi",'x',23},
{"automan",'x',34},
{"nengge",'y',43}
};
for(int i=0;i<5;i++)
{
fs_out.write((char *)&s[i],sizeof(Stu));
}
//这里有seekg和seekp,都是位置移动的函数,g是读的时候用,p是写的时候用,g就是get,p就是put
fs_out.seekg(0,ios::beg);
Stu tmp;
while(fs_out.read((char *)&tmp,sizeof(Stu)),!fs_out.eof())
{
cout<<tmp.name<<endl;
cout<<tmp.sex<<endl;
cout<<tmp.age<<endl;
}
fs_out.close();
}