C++输入输出流

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();


}

  • 14
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值