第17章 输入、输出和文件

 1.流和缓冲区

    C++程序把输入和输出看做字节流。输入时,程序从输入流中抽取字节;输出时,程序将字节插入到输出流中。通过缓冲区可以更高效地处理输入和输出。缓冲区是用作中介的内存块,它是将信息从设备传输到程序或从程序传输给设备的临时存储工具。输出时,程序首先填满缓冲区,然后把整块数据传输给硬盘,并清空缓冲区,以备下一批输出使用,这被称为刷新缓冲区。

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091026/1.jpg

    如果实现不能在所希望时刷新输出,可以使用两个控制符中的一个来强行进行刷新。控制符flush刷新缓冲区;而控制符endl刷新缓冲区,并插入一个换行符。事实上,控制符也是函数,因此下面的写法都是正确的:

2.重定向

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091026/2633921650646093750.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091026/3633921650647187500.jpg

3.使用cout进行输出

1)重载的<<操作符

     <<叫做插入操作符。它被重载,使之能够识别C++中所有的基本类型。插入操作符的所有化身的返回类型都是ostream &。也就是说,原型如下:  ostream & operator<< (type);   该操作符返回一个指向ostream对象的引用。

2)ostream类的put()和write()方法

     put()方法用于显示字符,它的原型如下:ostream & put(char);

     cout.put('i').put('t'); //打印it

     在原型合适的情况下,可以将数值型参数(例如int)用于put(),让函数原型自动将参数转换为正确的char值。如:cout.put(65); //显式ASKII码为65的字符A。cout.put(66.3); //将double值66.3转换为char值66,并显式ASKII码为66的字符B。
     write()方法显式整个字符串,其模板原型为:basic_ostream<charT, traits>& write(const char_type* s, streamsize n);。第一个参数提供了要显示的字符串的地址,第二个参数指出要显示多少个字符。write()方法并不会在遇到空字符时自动停止打印字符,而只是打印指定数目的字符,即使超出了字符串的边界。如:

3)用cout进行格式化

      ostream插入操作符将值转换为文本格式。浮点数的默认行为有变化,新式C++标准如下:浮点类型被显示为6位,末尾的0不显示(显示的数字位数与数字被存储时精读没有任何关系)。数字以定点表示还是以科学计数法表示,取决于它的值。具体来说,当指数大于等于6或小于等于-5时,将使用科学计数法。另外,字段宽度恰好容纳数字和负号(如果有的话)。

      ios_base类中的成员和方法以前位于ios类中。现在,ios_base是ios的基类。在新系统中,ios是包含char和wchar_t具体化的模板,而ios_base包含了非模板特性。

修改显示时使用的计数系统:

      要控制整数以十进制、十六进制还是八进制显示,可以使用dec、hex和oct控制符。cout<<hex;将cout对象的计数系统格式状态设置为十六进制,直到将格式状态设置为其他选项位置。hex(cout)与之等价。该控制符不是成员函数,因此不必通过对象来调用。

调整字段宽度:

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091026/4633921710367968750.jpg

      C++永远不会截短数据。如果试图在宽度为2的字段中打印一个7位值,C++将增宽字段,以容纳该数据。

 https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091026/5633921712587187500.jpg

填充字符:

      用fill()成员函数来改变填充字符。如cout.fill('*'); 与字段宽度不同的是,新的填充字符将一直有效,直到更改它为止。

设置浮点数的显示精度:

      浮点数精度的含义取决于输出模式。默认模式下,指的是总位数。在定点模式和科学模式下,精度指的是小数点后面的位数。C++默认精度为6位(不过末尾的0将不显示)。precision()成员函数使得能够选择其它值。与width()不同,与fill()相同,新的精度设置将一直有效,直到被重新设置。

谈setf():

       ios_base类有一个受保护的数据成员,其中的各位(这里叫标记)分别控制着格式化的各个方面。打开一个标记称为设置标记,并意味着相应的位被设置为1。例如hex、dec和oct控制符调整控制计数系统的3个标记位。setf()函数提供了另一种调整标记位的途径。

       bitmask类型是一种用来存储各个位置的类型。它可以是整型、枚举,也可以是STL bitset容器。主要思想是,每一位都是可以单独访问的,都有自己的含义。iostream软件包使用bitmask来存储状态信息。

      setf()函数有两个原型,第一个为:

       fmtflags是bitmask类型的typedef名,用于存储格式标记。这个版本的setf()用来设置单个位控制的格式信息。参数是一个fmtflags值,指出要设置哪一位。返回值是类型为fmtflags的数字,指出所有标记以前的设置。如果打算以后恢复原始设置,则可以保存这个值。修改将一直有效,直到被覆盖为止。那么应该给setf()传递什么呢?如果要第11位设置为1,则可以传递一个第11位为1的数字。对位进行跟踪单调乏味,ios_base类定义了代表位值的常量:

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/1.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/2.jpg

        第二个setf()原型接受2个参数,并返回以前的设置:

      函数的这种重载格式用于设置由多位控制的格式选项。第一参数和以前一样,也是一个包含了所需设置的fmtflags值。第二参数指出要清除第一个参数中的哪些位。例如,将第3,4位设置为1分别表示以10和8为基数。假设输出是以10为基数的,要将它设置为以8为基数,则不仅需要将第4位设置为1,还需要将第3位设置为0.具体地说,要修改基数,可以将常量ios_base::basefield用作第二参数,将ios_base::oct用作第一参数。

      ios_base类定义了可按这种方式处理的3组格式标记。每组标记都由一个可用作第二参数的常量和2~3个可用作第一参数的常量组成。第二参数清除一批相关的位,然后第一参数将其中一位设置为1。

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/3.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/4.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/5.jpg

标准控制符:

       使用setf()不是进行格式化的对用户最友好的方法,C++提供了多个控制符,能够调用setf(),并自动提供正确的参数。例如dec、hex和oct等。下面的语句:cout<<left<<fixed; 表示打开左对齐和定点选项。

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/6.jpg

iomanip头文件:

       C++在iomanip头文件中提供了其它一些控制符,它们表示起来更方便。3个最常用的是setprecision(),setfill()和setw()分别用来设置精度、填充字符和字段宽度。

4.使用cin进行输入

      可以将hex、oct和dec控制符与cin一时使用,比如cin>>hex; 将输入12或0x12解释为十六进制的12或十进制的18,而将ff或FF解释为十进制的255。不同版本的抽取操作符查看输入流的方法是相同的。它们跳过空白(空格、换行符和制表符),知道遇到非空白字符。在单字符模式下,>>操作符将读取该字符,将它放置到指定的位置。在其他模式下,>>操作符将读取一个指定类型的数据。也就是说,它读取从非空白字符开始,到与目标类型不匹配的第一个字符之间的全部内容。

      istream类为下列字符指针类型重载了>>抽取操作符:signed char* ; char* ; unsigned char* ; 对于这种类型的参数,抽取操作符将读取输入中的下一个单词,将它放置到指定的地址,并加上一个空值字符,使之成为一个字符串。

1)流状态

      cin或cout对象包含一个描述流状态(stream state)的数据成员(从ios_base类那里继承的)。流状态(被定义为iostate类型,是一种bitmask类型)由3个ios_base元素组成:eofbit、badbit和failbit,其中每个元素都是一位。当全部3个状态位都设置为0时,说明一切顺利。程序可以检查流状态,并使用这种信息来决定下一步做什么。

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/1633922294822500000.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/2633922298095468750.jpg

2)单字符输入

      在使用char参数或没有参数的情况下,get()方法读取下一个输入字符,即使该字符是空格、制表符或换行符。get(char & ch) 版本将输入字符赋给其参数,而get(void)版本将输入字符转换为整型(通常是int),并将其返回。注意,在本节里介绍的,get()指的是get(char & ch)和get(void)两个版本的总称,而get(void)在实际编程应用时表现形式是get()。

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/1633922355372031250.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/2633922355373125000.jpg

      get(void)成员函数的返回类型是int(或某种更大的整型,这取决于字符集和区域)。这使得下面的代码是非法的:cin.get().get(); 不过可以在抽取序列的最后使用get(): cin.get(c1).get();

       到达文件尾时,get(char &)将调用setstate(failbit),导致cin的测试结果为false。而cin.get(void)将返回EOF----iostream头文件提供的一个符号常量。程序可以这样写:int ch; while (( ch = cin.get() ) != EOF ) { ... } 注意ch的类型为int,而不是char。

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/3633922362722656250.jpg

      假设可以选择>>, get(char &),get(void),应使用哪一个呢?首先,如果希望跳过空白,则使用抽取操作符>>;如果希望程序检查每个字符,则使用get()方法。在get()方法中,get(char &)的接口更佳,而get(void)的主要优点是:它与标准C中的getchar()函数及其相似,这意味着可以通过包含iostream(而不是stdio.h),并用cin.get()替换所有的getchar(),用cout.put(ch)替换所有的putchar(ch),来将C程序转换为C++程序。

3)字符串输入:getline(),get(),ignore()

      getline()成员函数和get()的字符串读取版本都读取字符串,它们的函数特征标相同:

      第一个参数是用于放置输入字符串的内存单元的地址。第二个参数比要读取的最大字符数大1。第三个参数指定用作分界符的字母,只有两个参数的版本将换行符用作分界符。上述函数都在读取最大数目的字符或遇到换行符后为止。

      例如:char line[50]; cin.get(line,50); 表示将输入读取到字符数组line中,cin.get()函数将在到达第49个字符或遇到换行符(默认情况)后停止将输入读取到数组中。get()和getline()之间的主要区别在于:get()将换行符留在输入流中,这样接下来的输入操作首先看到的将是换行符,而getline()抽取并丢弃输入流中的换行符。

      对于3个参数的版本,第3个参数用于指定分界符。遇到分界字符后,输入将停止,即使还未读取最大数目的字符。因此,在默认情况下,如果在读取指定数目的字符之前到达行尾,这两种方法都将停止读取输入。get()将分界字符留在输入队列中,而getline()不保留。

      ignore()函数有两个参数,一个是数字,指定要读取的最大字符数;另一个是字符,用作输入分界符。如:cin.ignore(255,'/n'); 将读取并丢弃接下来的255个字符或直到到达第一个换行符。原型为:istream & ignore ( int = 1, int = EOF ); 原型默认值为1和EOF.EOF导致ignore()读取指定数目的字符或读取到文件尾。cin.ignore(8255.'/n').ignore(8255,'/n'); // 有可能将一共读取两行。

4)意外字符串输入(无输入情况和输入到达或超过函数指定最大字符数情况):

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/1633922473954687500.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/2633922473955937500.jpg

5)其他istream方法:read(),peek(),gcount(),putback()

     与getline()和get()不同的是,read()不会在输入后加上空值字符,因此不能将输入转换为字符串。read()方法不是为键盘输入设计的,它常与ostream write()函数结合使用,来完成文件输入和输出。如: char gross[144]; char score[20]; cin.read(gross,144).read(score,20); 。

     peek()函数返回输入中的下一个字符,但不抽取输入流中的字符。也就是说,它使得能够查看下一个字符。假如要读取输入,直到遇到换行符或句点,程序如下:

      gcount()方法返回最后一个非格式化抽取方法读取的字符数。这意味着字符是由get(),getline(),ignore(),read()方法读取的,不是由抽取操作符(>>)读取的,抽取操作符对输入进行格式化,使之与特定的数据类型匹配。

      putback()函数将一个字符插入到输入字符串中,被插入的字符将是下一条输入语句读取的第一个字符。使用peek()的效果相当于先使用get()读取一个字符,然后使用putback()将该字符放回到输入流中。不过,putback()允许将字符放到不是刚才读取的位置。

     下面程序采用两种方式来读取并显示输入中#字符(不包括)之前的内容。第一种方法先读取#再用putback将它插回到输入中,第二张方法在读取之前都peek()查看下一个字符。

      下面程序使用peek()来确定是否读取了整行。如果一行中只有部分内容被加入到输入数组中,程序将删除余下的内容。

5.文件输入和输出

     要让程序写入文件,必须这样做:

1)创建一个ofstream对象来管理输出流。

2)将该对象与特定的文件关联起来。

3)以使用cout的方式使用该对象,唯一的区别是:输出将进入文件,而不是屏幕。

     读取文件的要求与写入文件相似:

1)创建一个ifstream对象来管理输入流。

2)将该对象与特定的文件关联起来。

3)以使用cin的方式使用该对象。

     声明对象并和文件关联起来均可以用1条语句或2条语句完成:

      当程序在创建像fout这样的ofstream对象时,将为输出缓冲区分配空间。如果创建了两个ofstream对象,程序将创建两个缓冲区,每个对象各一个。

      当输入和输出流对象过期(如程序终止)时,到文件的连接将自动关闭。另外,也可以用close()方法来显式地关闭到文件的连接,关闭这样的连接并不会删除流,而只是断开流到文件的连接。不过,流管理装置仍被保留。例如,fin对象与它管理的输入缓冲区仍然存在。稍后可以知道,可以将流重新连接到同一个文件或另一个文件。

      如果需要同时打开两个文件,则必须为每个文件创建一个流。例如将两个排序后的文件拼接成第三个文件的程序,需要为两个输入文件创建两个ifstream对象,并为输出文件创建一个ofstream对象。可以同时打开的文件数取决于操作系统,通常为20个左右。不过,可能要依次处理一组文件,这种情况下,可以打开一个流,并将它依次关联到各个文件。在节省计算机资源方面,比为每个文件打开一个流的效率高。下面是依次读取两个文件的代码:

1)命令行处理技术

      C++有一种让在命令行环境中运行的程序能够访问命令行参数的机制,方法是使用下面的main()函数: int main(int argc, char *argv[]); argc为命令行中的参数个数,其中包括命令名本身。argv变量为一个指针,它指向一个指向char的指针。这过于抽象,但可以将argv看作一个指针数组,其中的指针指向命令行参数,argv[0]是命令行中的第一个字符串,依次类推。例如,假设有下面的命令行:“ wc report1 report2 report3   ” 则argc为4,argv[0]为wc,argv[1]report1,依此类推。下面的循环将把每个命令行参数分别打印在单独一行上: for(int i=1;i<argc;i++) cout<<argv[i]<<endl; //以i=1开头将只打印命令行参数。以i=0开头将同时打印命令名。

       下面程序结合命令行技术和文件流技术,来计算命令行上列出的文件包含的字符数:

 

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/3633922555212343750.jpg

2)文件模式

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/6633922560584062500.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/7.jpg

      下面是一个在文件尾追加数据的程序:

 

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/8.jpg

3)二进制文件

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/10.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/11.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/12.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/13.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091027/14.jpg

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值