格式标志
ios类是所有ios类层次的基类,它提供了所有派生类中都需要的流的状态设置、状态报告成员函数,以及精度、域宽设置等成员函数。接下来分别进行学习。
1、格式化状态
在编程中,常常用一个字节(byte)或几个字节来标志某些属性,每个位标志着一个属性,例如,一个字节有8位:00000000,那么我们可以用第一位(右起)表示是否吃饭,1表示吃饭,0表示未吃饭;用第二位表示是否梳洗,1表示梳洗,0表示未梳洗;第三位表示是否上学,1表示上学,0表示未上学,其他各位也可定义相应的含义。如设置状态为00000101,该值就表示,吃了饭,没有洗漱,去上学。这样来标志属性很节省空间,表示起来简单又方便。
在C++中,I/O的输入输出状态就是通过每一个二进制位来设置的,ios基类为流定义了一组状态标志,如下所示:
class ios_base
{
public:
//……
typedef implementation_defined1 fmtflags;
static const fmtflags
skipws, //输出时跳过空白
left, //输出左对齐
right, //输出右对齐
internal, //在符号和值之间填充
boolalpha, //用符号形式表示真假
dec, //以十进制输出
hex, //以十六进制输出
oct, //以八进制输出
scientific, //用科学计数法输出浮点数
fixed, //用定点数方式输出实数
showbase, //输出前缀,八进制加0,十六进制加0x
showpoint, //输出浮点数时总是带小数点
showpos, //输出正整数时加"+"
uppercase, //输出十六进制时所有字母均用大写
adjustfield, //与域调整有关的标志组
basefield, //与整数基数有关的标志组
floatfield, //与浮点数输出有关的标志组
unitbuf; //每次输出操作之后刷新
//……
};
如果设置了某个状态标志位,则对应位为1,否则为0。例如,如果设置了skipws,则相应的二进制位值就为1。
2、格式化操控符
ios类还提供了数个成员函数,用于定义各种I/O格式,实现访问格式标志的功能,这些函数
包括setf()、unsetf()、flags()、flags(flags),它们可以处理所有的格式定义,接下来我们分别来学习这几个函数。
(1)setf()函数
setf()函数用于设置状态标志,把指定位的状态标志设置为1,其函数声明有两种形式,如下所示:
fmtflags ios::setf(fmtflags flag); //第一种形式
fmtflags ios::setf(fmtflags flag, fmtflags mask); //第二种形式
● 第一种形式是用flag设置流的格式,但不会减少之前的流标志,也就是说相当于在原来的流标志的基础上添加flag标志。
● 第二种形式是用flag和mask两个参数共同设定流格式标记,清除mask标志位中不属于flag中的标志。
例如,若要输出数据为十六进制数,则可在输出语句前加如下语句:
cout.setf(ios::hex, ios::basefield);
该语句将状态标志的hex对应位设置为1。当一次要设置多个状态位时,用位“或”符号(|)连接要设置的状态标志位,代码示例如下所示:
cout.setf(ios::scientific | ios::uppercase);
cout << 2006.5 << endl;
在输出时设置状态标志是以科学计数法输出浮点数,并且输出时所有字母均用大写字母。
(2)unsetf()函数
unsetf()函数用于清除状态标志,其函数声明如下所示:
void ios::unsetf(fmtflags mask);
该成员函数的功能是把指定位的状态标志设置为0。例如,在输出数据时设置了showpos位,如果要取消显示“+”符号的状态标志,则可调用unsetf()函数来取消设置,代码如下所示:
cout.unsetf(ios::showpos);
(3)flags()函数
flags()函数用于获取流状态标志位,其函数声明有两种形式,如下所示:
fmtflags ios::flags() const;
fmtflags ios::flags(fmtflags flag);
● 第一种形式调用是返回当前流状态标志。
● 第二种形式调用,返回设置前的状态标志位,然后将这些状态位清除,再以参数作为新格式标志状态。
函数flags()非常有用,它可用于存储当前标志状态,并在适当时机恢复。
学习完上面几个函数,接下来我们通过一个案例来演示其用法,具体代码如例1所示:
例1
1 #include <iostream>
2 using namespace std;
3 int main()
4 {
5 float f = 20.2; //定义一个float类型变量
6
7 //设置以科学计数法输出,所用字母均是大写
8 cout.setf(ios::scientific | ios::uppercase);
9 cout <<"f = "<< f << endl;
10
11 //用flags()函数获取当前流状态标志
12 cout <<"第一次获取流状态标志:"<< cout.flags() << endl;
13 cout.flags(ios::showpos); //将先前的状态标志都清除,设置showpos标志,
14
15 //再次调用flags()获取当前流状态标志
16 cout << "第二次获取流状态标志:"<<cout.flags() << endl;
17 cout << "设置showpos后,f = "<<f << endl;
18 cout.unsetf(ios::showpos); //取消showpos标志设置
19 cout <<"unsetf后, f = "<< f << endl;
20 system("pause");
21 return 0;
22 }
运行结果如图1所示。
图1 例1的运行结果
在例1中定义了一个变量f,然后设置流状态标志scientific和uppercase,以科学计数法输出,并且所用字母都为大写,由图1可以看出,输出的f的格式发生了改变。然后代码12行调用flags()函数获取当前流的状态标志为4613,它对应的二进制数,表示着流的状态设置。代码第13行又调用flags()函数设置showpos位,它会把先前的标志(如scientific等)清除,再设置showpos位,设置后输出当前流状态为+32,流状态发生了改变,而输出f的值也变为+20.2。最后第18行代码调用unsetf()取消showpos位的设置,则输出f的值恢复到原来形式20.2。
多学一招:流状态复制函数
copyfmt()函数
copyfmt()函数实现从流中复制所有的格式定义,其函数声明如下所示:
ios_base& copyfmt(const basic_ios& f);
copyfmt()并不复制流的缓冲区和缓冲区的状态,但是它复制状态中其余的东西,包括所要求的异常和用户给状态添加的所有东西。