基本技能11.4:使用ios的成员函数进行格式化输入和输出
每一个流都有与之相关联的一套格式化标记,用来控制流对信息的格式化方式。类ios中定义了位图形式的枚举类型fmtflags,其中定义了下面我们要使用的值。(从技术的角度来讲,这些值是定义在ios_base类中的,它是ios类的基类。)
这些值可以被用来设置或者清除格式化标记。一些旧的编译器中可能没有定义fmtflags这个枚举类型。此时,这些格式化标记将被以长整形数的形式进行编码。
当标记skipws被设置后,在对流进行输入的时候空白字符(空格,tab符以及换行符)符将会被忽略。当标记skipws被清除后,这些空白字符将不会被忽略。
当标记left被设置后,输出将按照左对齐的方式进行。当标记right被设置的时候,输出将按照右对齐的方式进行。如果这两个标记都没有被设置,输出则按照右对齐的方式进行。
当标记internal被设置后,数值的符号将会采用左对齐的方式,而数值采用右对齐的方式进行输出。
缺省情况下,数字都是以10进制的格式输出的。然而我们是可以修改基数的。通过设置oct标记可以使得数值以八进制的形式输出;设置hex标记可以使得数值以16进制的格式进行输出。再次设置dec标记则可以恢复到缺省的10进制输出格式。
设置showbase标记则会在输出数值的时候也输出基数。比如,十六进制的1F在以16进制的格式输出的时候就是0x1F。
缺省情况下,当以科学计数法的形式输出数值的时候,字符e是以小写的形式出现的。同样当以十六进制的格式输出数值的时候x也是以小写的形式出现的。当设置了uppercase标记后,这些字母都会以大写的形式出现。
设置showpos标志后,在输出整数的时候会在前面增加+号。设置showpoint标志后,在输出浮点数的时候不管是否需要都会输出十进制的小数点部分。
设置了scientific标志后,浮点数将会以科学技术法的格式进行输出。设置了fixed标志后,将固定的输出小数点后面的小数部分(小数部分输出的位数由设置的精度来确定)。如果这两个标记都没有被设置,编译器将自己选择一个合适的输出格式。
当设置了unitbuf标记后,输入输出缓冲区将在每一次插入操作之后都立刻把缓冲区中的数据进行输入输出。当boolalpha标记被设置后,则在输出布尔值的时候会输出true或者false,输入的时候也可以输入true或false。
最后,标记scientific 和fixed则可以通过floatfield标记来一次性设置。
设置和清除格式化标记
我们可以使用函数setf()来设置一个标志。这个函数是ios的成员函数,它的常用形式如下:
fmtflags setf(fmtflags flags);
该函数打开由flags所指示的标记并返回先前的格式标记。例如,需要打开showbase(该标记用于输出时显示整数字段基数的前缀)标记时,我们可以使用如下语句:
stream.setf(ios::showbase);
其中,stream就是我们想要设置其格式标记的流对象。注意,这里使用到了iso::来修饰showbase。这是因为showbase是由ios类定义的一个枚举类型的常量,在引用这个常量的时候我们必须使用ios来进行限定。这种原则适用于所有的格式化标记。
下面的程序演示了如何使用setf()函数来同时打开showpos(该标记用于输出时在非负的数字字段前加上一个+符号)和scientific(用科学计数法的方式来显示浮点数)标记:
#include <iostream>
using namespace std;
int main()
{
//打开showpos和scientific标记
cout.setf(ios::showpos);
cout.setf(ios::scientific);
cout<< 123 << " " << 123.23 << "";
return 0;
}
上面程序的输出如下:
+123 +1.232300e+002
我们还可以在一次调用setf()函数的时候把多个标记用OR运算符连接起来,一次性进行设置。例如,通过下面的语句中的把showpos和scientific进行一次性设置:
cout.setf(ios::showpos | ios::scientific);
需要关闭格式标记的时候可以使用unsetf()函数。它的原型如下:void unsetf( fmtflags flags); 由flags所指定的所有标记将会被关闭。
有时我们需要查询当前的格式标记设置情况,此时我们可以使用flags()函数来获取当前的标记状态。它的原型如下:fmtflags flags();
这个函数返回与当前相关流的标记的当前值。flags()函数还有一种用法如下:fmtflags flags(fmtflags flags); 这种用法根据flags参数设置相关流的格式化参数,并返回之前的格式化标记值。
下面的程序演示了如何使用flags()和unsetf()函数:
//演示flags()和unsetf()函数的用法
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
ios::fmtflags f;
f = cout.flags();//获取当前的格式标记值
if ( f&ios::showpos)
{
cout << "showpos is set for count.\n";
}
else
{
cout << "showpos is cleared for cout.\n";
}
cout << "\nSetting showpos for cout.\n";
cout.setf(ios::showpos);//设置showpos标记
f = cout.flags();
if ( f & ios.showpos )
{
cout << "showpos is set for count.\n";
}
else
{
cout << "showpos is cleared for cout.\n";
}
cout <<"/nClearing showpos for cout.\n";
cout.unsetf(ios::showpos);
f = cout.flags();
if ( f & ios.showpos )
{
cout << "showpos is set for count.\n";
}
else
{
cout << "showpos is cleared for cout.\n";
}
return 0;
}
上面程序的输出结果如下:
showpos is cleared for cout.
Setting showpos for cout.
showpos is set for count.
Clearing showpos for cout.
showpos is cleared for cout.
在上面的程序中,在使用类型fmtflags声明变量f的时候,类型前面使用了ios::进行限定。这样做是必要的,这是因为fmtflags是由ios类定义的类型。通常情况下,当我们使用一个由类定义的类型名称或者枚举常量的时候,我们必须使用该类的名称来限定它。
设置宽度,精度和填充字符
除了上面谈到的格式化标记外,ios类还提供了另外的三个函数,可以用来设置附加的格式控制信息:流的宽度,精度和填充字符。这三个函数分别是:width(), precision()和fill()。我们将逐一进行讨论。
缺省情况下,当输出一个值的时候,它只占用对应数量的字符空间。然而,我们可以通过使用width()函数来指定它所占用的最小宽度。该函数的原型如下:
streamsize width(streamsize w);
其中,w指定了新的输出宽度,之前的输出宽度由函数返回。在某些实现中,流宽度的值必须在每次输出前都被设置。否则就使用缺省的宽度值。其中的streamsize是由编译器定义的整形类型。
在设置了流的最小宽度以后,如果当一个值输出时所占用的空间小于该指定的最小宽度,多余的空间就会被用当前填充字符进行填充(在缺省情况下填充字符就是空格)。如果一个值的输出空间超出了指定的最小宽度,则使用需要的真实空间数量进行输出,而不会对输出结果进行截断。
当以科学计算法输出浮点数的时候,我们可以使用precision()函数来设置小数点后面的小数位数。该函数的原型如下:
streamsize precision(streamsize p);
其中,p就是指定的精度,函数返回之前的精度值。缺省的精度为6。在一些实现中,必须在每次输出浮点数之前都调用该函数来设置精度,否则将使用缺省精度进行输出。
缺省情况,如果输出的时候需要进行填充,则采用空格进行填充。我们可以通过使用函数fill()来指定填充字符。它的原型如下:
char fill(char ch);
调用该函数后,字符ch将成为新的填充字符,函数返回之前的填充字符。
下面的程序演示了上面三个函数的使用方法:
//演示width(),precision()和fill()函数的用法
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
cout.setf(ios::showpos);
cout.setf(ios::scientific);
cout << 123 << " " << 123.23 << "\n";
cout.precision(2); //十进制小数点后两位数
cout.widen(10); //10个字符的宽度
cout << 123 << " ";
cout.widen(10); //10个字符的宽度
cout << 123.23 << "\n";
cout.fill('#');//设置填充字符为#
cout.width(10);//10个字符的宽度
cout << 123 << " ";
cout.width(10);//10个字符的宽度
cout << 123.23;
return 0;
}
上面程序的输出结果如下:
+123 +1.232300e+002
+123 +1.23e+002
######+123 +1.23e+002
正如我们说过的那样,由于一些实现上的差异,在每次输出之前都必需重新设置输出宽度。这也是上面程序中为什么反复调用width()函数的原因。这三个函数还有重载的形式,可以用来获取而不是修改当前的设置。其形式如下:
char fill();
streamsize width();
streamsize precision();
练习
1. boolalpha是用来做什么的?
2. setf()是用来做什么的?
3. 我们是用什么函数来设置填充字符?