C 语言中,我们可以通过函数 printf() 和 scanf() 进行格式化控制,而在 C++ 中仍然可继续使用二者,但还提供了以下两种格式控制方式:
- 使用流成员函数进行格式控制
- 使用预定义操作符进行格式控制
1.流成员函数
流成员函数主要指 ios 类(流基类)中的成员函数,分别有:
(1)设置状态标志流成员函数 setf()。
// 函数原型
long ios::setf(long flags)
// 调用格式
流对象.setf(ios::状态标志)
ios 类的状态标志有:
常量 | 含义 | failbit | eofbit | badbit | 十进制值 |
---|---|---|---|---|---|
ios::failbit | I/O流出现致命错误,可挽回 | 1 | 0 | 0 | 4 |
ios::eofbit | 已到达文件尾 | 0 | 1 | 0 | 2 |
ios::badbit | I/O流出现致命错误,不可挽回 | 0 | 0 | 1 | 1 |
ios::goodbit | 流状态正常 | 0 | 0 | 0 | 0 |
因为状态标志在 ios 类中定义为枚举值,所以在引用这些值前要加上ios::
。如果有多项标志,中间则用"|"分隔。
(2)清除状态标志流成员函数 unsetf()。
一般格式:long ios::unsetf(long flags)
调用格式:流对象.unsetf(ios::状态标志)
(3)设置域宽流成员函数 width()。
一般格式:int ios::width(int n)
调用格式:流对象.width(n); // 它只对下一个流输出有效,输出完成后,恢复默认值 0
(4)设置实数精度流成员函数 precision()。
一般格式:int ios::precision(int n)
调用格式:流对象.precision(n);// 参数 n 在十进制小数形式输出时代表有效数字。在以 fixed 形式和 scientific 形式输出时代表小数位数
(5)填充字符流成员函数 fill()。
一般格式:char ios::fill(char ch)
调用格式:流对象.fill(ch); // 当输出值不满宽域时用填充符来填充,默认填充符为空格,它与 width 函数搭配。
参考如下示例:
#include "stdafx.h"
#include <iostream>
#include <string>
int main() {
std::cout.setf(std::ios::left|std::ios::showpoint|std::ios::unitbuf);
std::cout.precision(6);
std::cout<<123.45678;
std::cout.width(50);
std::cout.fill('-');
std::cout.unsetf(std::ios::left);// 清除状态左对齐
std::cout.setf(std::ios::right);
std::cout<<"十进制小数输出,有效数字为6位"<<std::endl;
std::cout.setf(std::ios::left|std::ios::fixed);
std::cout.precision(6);
std::cout<<123.45678;
std::cout.width(50);
std::cout.fill('-');
std::cout.unsetf(std::ios::left|std::ios::fixed);// 清除状态左对齐和定点格式
std::cout.setf(std::ios::right);
std::cout<<"固定小数位fixed,小数位为6位"<<std::endl;
std::cout.setf(std::ios::left|std::ios::scientific);
std::cout.precision(6);
std::cout<<123.45678;
std::cout.width(50);
std::cout.fill('-');
std::cout.unsetf(std::ios::left|std::ios::scientific);//清除状态左对齐和科学计数法格式
std::cout.setf(std::ios::right);
std::cout<<"科学计数法表示,小数位为6位"<<std::endl;
std::cout.fill(' ');//设置填充符为默认空格
std::cout.unsetf(std::ios::right);//清除状态靠右对齐
std::cout.setf(std::ios::dec|std::ios::showpos|std::ios::internal);//设置状态基数为10,正整数前显示"+"和数据符号左对齐,数据本身右对齐,数据和符号之间为填充符' '
std::cout.width(6);
std::cout<<128<<std::endl;
std::cout.unsetf(std::ios::dec);//清除状态基数为10
//在输出整数的八进制形式或十六进制形式之前,先要把默认的十进制形式的标志清除std::cout.unsetf(std::ios::dec)
std::cout.setf(std::ios::oct|std::ios::showbase);//设置状态基数为8,输出整数时显示基数符号
//std::ios::internal标志对八进制不起作用
std::cout<<128<<std::endl;
std::cout.unsetf(std::ios::oct);//清除状态基数为8
std::cout.setf(std::ios::hex|std::ios::uppercase);//设置状态基数为16,输出整数时显示基数符号,科学计数法输出时E大写,十六进制字母大写
//std::ios::internal标志对十六进制不起作用
std::cout<<255<<std::endl;
std::cout.unsetf(std::ios::hex);//清除状态基数为16
return 0;
}
输出结果:
123.457----------------------十进制小数输出,有效数字为6位
123.456780-----------------------固定小数位 fixed,小数位为6位
1.234568e+02-----------------------科学计数法表示,小数位为6位
+ 128
0200
0XFF
2.操纵符
用 ios 类中的成员函数来进行 IO 格式的控制总需要写一条单独的语句,而不能直接嵌入到 IO 语句中去,显得很不方便。因此 C++ 又提供了一种用操纵符来控制 IO 格式。
操纵符分为带参和不带参的两种,带参的定义在头文件<iomanip>
中,不带参的定义在<iostream>
中。下面是 C++ 中预定义的操作符:
-
dec 设置整数基数为10,用于输出和输入;
-
hex 设置整数基数为16,用于输出和输入;
-
oct 设置整数基数为8,用于输出和输入;
-
ws 跳过输入的空格符,用于输入;
-
endl:输出一个换行符并刷新输出流,用于输出;
-
ends 插入一个空字符 null,通常用来结束一个字符串,用于输出;
-
flush 刷新一个输出流,用于输出;
-
setbase(n) 设置整数的基数为 n (可取0或10代表十进制,8代表八进制和16代表十六进制,默认为0),用于输入和输出;
-
setfill( c ) 设置填充符,默认为空格,用于输出;
-
setprecision(n) 设置实数精度n,原理和成员函数precision一样,用于输出;
-
setw(n) 设置域宽 n,用于输出;
-
setiosflags(flags) 设置指定状态标志,多个用"|"分隔,用于输出和输入;
-
resetiosflags(flags) 清除指定状态标志,多个用"|"分隔,用于输出和输入;
操作符 setiosflags(flags) 和 resetiosflags(flags) 的部分状态标志:
状态标志 | 功能 |
---|---|
left | 按域宽左对齐输出 |
right | 按域宽右对齐输出 |
fixed | 定点格式小数输出 |
scientific | 科学计数法输出 |
showpos | 在正数显示“+” |
uppercase | 在以科学计数法和以十六进制输出时字母用大写表示 |
参考如下示例:
#include "stdafx.h"
#include <iostream>
#include <iomanip> // 带形参的操纵符必须含有该头文件
#include <string>
int main() {
std::string str="abcdefg";
std::cout<<str<<std::ends<<std::endl;//std::ends用来结束一个字符串
std::cout<<std::setiosflags(std::ios::left|std::ios::showpoint|std::ios::unitbuf)<<std::setprecision(6)<<123.45678<<std::setw(50)<<std::setfill('-')<<std::resetiosflags(std::ios::left)<<std::setiosflags(std::ios::right)<<"科学计数法表示,小数位为6位"<<std::endl;
std::cout<<std::setiosflags(std::ios::left|std::ios::fixed)<<std::setprecision(6)<<123.45678<<std::setw(50)<<std::setfill('-')<<std::resetiosflags(std::ios::left|std::ios::fixed)<<std::setiosflags(std::ios::right)<<"固定小数位fixed,小数位为6位"<<std::endl;
std::cout<<std::setiosflags(std::ios::left|std::ios::scientific)<<std::setprecision(6)<<123.45678<<std::setw(50)<<std::setfill('-')<<std::resetiosflags(std::ios::left|std::ios::scientific)<<std::setiosflags(std::ios::right)<<"科学计数法表示,小数位为6位"<<std::endl;
std::cout<<std::setfill('')<<std::resetiosflags(std::ios::right)<<std::flush;//std::flush刷新一个输出流
std::cout<<std::dec // 或 std::setbase(10或0)
<<std::setiosflags(std::ios::showpos|std::ios::internal)<<std::setw(6)<<128<<std::endl;
std::cout<<std::setbase(8)// 或 std::oct
<<std::setiosflags(std::ios::showbase)<<128<<std::endl;
std::cout<<std::setbase(16)// 或 std::hex
<<std::setiosflags(std::ios::showbase|std::ios::uppercase)<<255<<std::endl;
return 0;
}
输出结果:
abcdefg
123.457-----------------------科学计数法表示,小数位为6位
123.456780-----------------------固定小数位fixed,小数位为6位
1.2345678e+002-----------------------科学计数法表示,小数位为6位
+ 128
0200
0XFF
3.自定义操纵符
除了利用系统预定义的操纵符来进行IO格式的控制外,用户还可以自定义操纵符来合并程序中频繁使用的IO写操作。定义形式如下:
输出流自定义操纵符:
ostream &操纵符名(ostream &s) {
自定义代码
return s;
}
输入流自定义操纵符:
istream &操纵符名(istream &s) {
自定义代码
return s;
}
返回流对象很关键,否则操纵符就不能用在流的 IO 链式操作序列中。
示例验证如下:
#include "stdafx.h"
#include <iostream>
#include <iomanip>
std::ostream& outputNo(std::ostream& s) {
//编号格式如:0000001
s<<std::setw(7)<<std::setfill('0')<<std::setiosflags(std::ios::right);
return s;
}
std::istream& To16(std::istream& s) {
//要求输入的数为十六进制数
s>>std::hex;
return s;
}
int main() {
std::cout<<outputNo<<8<<std::endl;
int a;
std::cout<<"请输入十六进制的数:";
std::cin>>To16>>a;
std::cout<<"转化为十进制数:"<<a<<std::endl;
return 0;
}
输出结果:
0000008
请输入十六进制的数:ff
转化为十进制数:255