C++格式标识和操纵器

设计程序时,我们通常需要将输出数据以某种格式显示出来,例如我们希望将时间显示为“dd:dd:dd”的形式,如此一来就需要借助格式标识符来控制 cout 对象的输出格式。

在 ios_base 类中,系统已经定义了很多格式标识符,通过这些标识符,我们可以很好地进行格式化控制,具体见下表。除此之外,ios_base 类中还定义有 width() 和 precision() 等函数,这些函数同样可以辅助我们进行格式化控制。

格式标识符 用途
boolalpha 以 true 和 false 的形式读写 bool 类型变量
dec 以十进制的方式进行读写
oct 以八进制的方式进行读写
hex 以十六进制的方式进行读写
internal 放在正负号或基数标识之后
left 左对齐
right 右对齐
fixed 以定点方式表示浮点数
scientific 用科学计数法表示浮点数
showbase 输出时显示基数
showpoint 必须显示小数点
showpos 对非负数显示+号
skipws 忽略空格
unitbuf 写操作完成后清扫所有的缓冲区
uppercase 用大写字母表示十六进制
adjustfield 格式标识必须是 left、right 或 internal
basefield 格式标识必须是 dec、oct 或 hex
floatfield 格式标识必须是 scientific 或 fixed

有了表中所列标识符,我们的确可以进行格式控制,但除了能够直接调用这些格式标识符之外,我们还可以借助类中提供的成员函数,具体成员函数见下表。

成员函数 用途
flags() 返回当前的格式标识
flags( val ) 将格式标识设置为 val,并返回旧值
setf ( val ) 将 val 设置为当前的格式标识,并返回旧的格式标识
setf ( val, ios_base::basefield ) 将 val 设置为当前的格式标识,并返回旧的格式标识,且要求 val 为 iso_base::dec、ios_base::hex 或 ios_base::oct 三者之一
setf ( val, ios_base::adjustfield ) 将 val 设置为当前的格式标识,并返回旧的格式标识,且要求 val 为 iso_base::left、ios_base::internal 或 ios_base::right 三者之一
setf ( ios_base::internal, ios_base::adjustfield ) 在符号与数据之间填充字符,并返回旧标识
setf ( ios_base::scientific,
ios_base::floatfield )
设置科学计数法并返回旧标识
setf ( ios_base::fixed,
ios_base::floatfield )
设置定点计数法并返回旧标识
setf ( 0, ios_base::floatfield ) 设置默认浮点数表示法并返回旧标识
unsetf ( val ) 清楚指定的标识并返回旧标识
 
这些函数都是 ios_base 类的成员函数,可以通过对象直接调用,而 cout 是 basic_ostream 的一个对象,basic_ostream 继承自 ios_base,因此 cout 可以调用这些函数。

【例 1】
#include<iostream>
using namespace std;

int main()
{
    ios_base::fmtflags old_val = cout.flags( ios_base::left | ios_base::hex |ios_base::uppercase | ios_base::showbase );

    for( int i = 100; i < 150; i ++ )
        cout << i << endl;
    cout<<endl<<endl;   
    cout.unsetf ( ios_base::left | ios_base::hex |ios_base::uppercase );

    cout.setf ( old_val );
    for( int i = 100; i < 150; i ++ )
        cout << i << endl;       
    cout<<endl<<endl;
    cout.unsetf( old_val );

    cout.setf ( ios_base::hex | ios_base::uppercase );
    cout << " hex " << 170 << endl;
    cout.unsetf( ios_base::hex | ios_base::uppercase );
    cout.setf ( ios_base::oct );
    cout << " oct " << 170 << endl;
    cout.unsetf( ios_base::oct );
    cout.setf ( ios_base::dec );
    cout << " dec " << 170 << endl;

    return 0;
}
本例中,我们先调用 flags() 函数设置输出格式,格式设置为左对齐和大写的十六进制,之后将 100~149 的数字全部输出,输出完之后将刚才设置的格式全部取消,然后重新设置为原始的格式 old_val,再将数字 100~149 的数字重新输出一遍。之后我们演示的是将 170 按照十六进制、八进制和十进制的形式输出。

在整个程序中,由于我们一开始设置了 ios_base::showbase,即显示基数,基数的意思就是说在十六进制前加上 0x,在八进制前加上 0,十进制则不作处理。我们一开始设置了 ios_base::showbase 后,一直没有取消设置,因此在整个程序输出过程中一直都是加上了基数。出现这种情况也是我们要注意的,一旦格式设置完成,则会一直保持格式,除非取消格式,因此在设置新格式前最好将原有设置的格式都取消。

【例 2】
#include<iostream>
using namespace std;

int main()
{
    cout.width ( 6 );
    cout << -100 << endl;

    cout.setf( ios_base::left ,ios_base::adjustfield);
    cout.width ( 6 );
    cout << -100 << endl;
    cout.unsetf( ios_base::left );

    cout.setf( ios_base::right ,ios_base::adjustfield);
    cout.width ( 6 );
    cout << -100 << endl;
    cout.unsetf( ios_base::right );

    return 0;
}
在本例中,我们调用成员函数 width() 控制输出的宽度为 6 个字符的位置,之后分别设置左对齐和右对齐,然后输出 -100。从上面程序中我们可以看出默认是采用右对齐的方式输出的。

当然,这些格式化控制除了可以使用格式标识来控制外,我们还可以使用操纵器来控制,下面就来简单了解一下操纵器。

操作器其本质是函数,它可以直接改变流的格式。例如:
cout << hex << uppercase << " hex " << 170 << endl;
cout << oct << " oct " << 170 << endl;
cout << dec << " dec " << 170 << endl;
本例中 hex、oct、dec 和 uppercase 都是操纵器,它可以与输入或输出操作符一起使用。

C++ 标准库中预定义了一些操纵器,使用其中带参数的操纵器则需要包含头文件<iomanip>,下标中列出了一些带参数的操纵器。

格式标识符 作用对象 功能
setbase( int n ) basic_ostream 将 n 设置为基数(默认值为 0)
setfill( char_type c ) basic_ostream 设置填充字符为 c
setprecision( int n ) basic_ostream 设置精度为 n
setw( int n ) basic_ostream 设置域宽为 n
setiosflags( val ) ios_base 设置指定的标识
resetiosflags( val ) ios_base 清楚指定的标识

表中列出的最后两个函数,其参数值为标识,即为格式标识符的组合。