C++的cout高阶格式化操作
这篇文章主要讲解如何在C++中使用cout进行高级的格式化输出操作,包括数字的各种计数法(精度)输出,左或右对齐,大小写等等。通过本文,您可以完全脱离scanf/printf,仅使用cout来完成一切需要的格式化输入输出功能(从非性能的角度而言)。更进一步而言,您还可以在<sstream>、<fstream>上使用这些格式化操作,从而代替sprintf和fprintf函数。为方便描述,下文仅以cout为例进行介绍。
一、综述
cout是STL库提供的一个iostream实例,拥有ios_base基类的全部函数和成员数据。进行格式化操作可以直接利用setf/unsetf函数和flags函数。cout维护一个当前的格式状态,setf/unsetf函数是在当前的格式状态上追加或删除指定的格式,而flags则是将当前格式状态全部替换为指定的格式。cout为这个函数提供了如下参数(可选格式):
- ios::dec 以10进制表示整数
- ios::hex 以16进制表示整数
- ios::oct 以8进制表示整数
- ios::showbase 为整数添加一个表示其进制的前缀
- ios::internal 在符号位和数值的中间插入需要数量的填充字符以使串两端对齐
- ios::left 在串的末尾插入填充字符以使串居左对齐
- ios::right 在串的前面插入填充字符以使串居右对齐
- ios::boolalpha 将bool类型的值以true或flase表示,而不是1或0
- ios::fixed 将符点数按照普通定点格式处理(非科学计数法)
- ios::scientific 将符点数按照科学计数法处理(带指数域)
- ios::showpoint 在浮点数表示的小数中强制插入小数点(默认情况是浮点数表示的整数不显示小数点)
- ios::showpos 强制在正数前添加+号
- ios::skipws 忽略前导的空格(主要用于输入流,如cin)
- ios::unitbuf 在插入(每次输出)操作后清空缓存
- ios::uppercase 强制大写字母
以上每一种格式都占用独立的一位,因此可以用“|”(位或)运算符组合使用。调用setf/unsetf或flags设置格式一般按如下方式进行:
1
2
|
cout.setf(ios::right | ios::hex); //设置16进制右对齐 cout.setf(ios::right, ios::adjustfield); //取消其它对齐,设置为右对齐 |
setf可接受一个或两个参数,一个参数的版本为设置指定的格式,两个参数的版本中,后一个参数指定了删除的格式。三个已定义的组合格式为:
- ios::adjustfield 对齐格式的组合位
- ios::basefield 进制的组合位
- ios::floatfield 浮点表示方式的组合位
设置格式之后,下面所有使用cout进行的输出都会按照指定的格式状态执行。但是如果在一次输出过程中需要混杂多种格式,使用cout的成员函数来处理就显得很不方便了。STL另提供了一套<iomanip>库可以满足这种使用方式。<iomanip>库中将每一种格式的设置和删除都进行了函数级的同名封装,比如fixed函数,就可以将一个ostream的对象作为参数,在内部调用setf函数对其设置ios::fixed格式后再返回原对象。此外<iomanip>还提供了setiosflags、setbase、setfill、setw、setprecision等方便的格式控制函数,下文会逐一进行介绍。大多数示例代码都会使用到<iomanip>,因此默认包含的头文件均为:
1
2
|
#include <iomanip> #include <iostream> |
二、缩进
将输出内容按指定的宽度对齐,需要用到ios::right、ios::left、ios::internal和iomanip里的setw。其中setw用于指定要输出内容的对齐宽度。以下两段代码的结果完全相同,前面是一个浮点数-456.98,后面紧跟着一个字符串“The End”以及换行符“endl”。
代码一:
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <iomanip> #include <iostream> using namespace std; int main( void ) { cout.flags(ios::left); //左对齐 cout << setw(10) << -456.98 << "The End" << endl; cout.flags(ios::internal); //两端对齐 cout << setw(10) << -456.98 << "The End" << endl; cout.flags(ios::right); //右对齐 cout << setw(10) << -456.98 << "The End" << endl; return 0; } |
代码二:
1
2
3
4
5
6
7
8
9
|
#include <iomanip> #include <iostream> using namespace std; int main( void ) { cout << left << setw(10) << -456.98 << "The End" << endl; //左对齐 cout << internal << setw(10) << -456.98 << "The End" << endl; //两端对齐 cout << right << setw(10) << -456.98 << "The End" << endl; //右对齐 return 0; } |
结果:
-456.98 The End
- 456.98The End
-456.98The End
这里要额外说明的一点是,setw函数会用当前的填充字符控制对齐位置,默认的填充字符是空格。可以通过<iomanip>的setfill来设置填充字符,比如下面的代码用字符“0”作为填充字符:
1
2
3
4
5
6
7
|
#include <iomanip> #include <iostream> using namespace std; int main( void ) { cout << setfill( '0' ) << setw(10) << 45698 << endl; return 0; } |
结果:
0000045698
三、整数
输出整数的格式有按不同进制数出:ios::hex(16进制)、ios::dec(10进制)、ios::oct(8进制),也可强制其输出符号(正数也加上“+”号前缀),对于16进制的输出还可配合ios::uppercase使所有字母以大写表示。代码示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include <iomanip> #include <iostream> using namespace std; int main( void ) { cout.setf(ios::showpos | ios::uppercase); cout << hex << setw(4) << 12 << setw(12) << -12 << endl; cout << dec << setw(4) << 12 << setw(12) << -12 << endl; cout << oct << setw(4) << 12 << setw(12) << -12 << endl; cout.unsetf(ios::showpos | ios::uppercase); cout << hex << setw(4) << 12 << setw(12) << -12 << endl; cout << dec << setw(4) << 12 << setw(12) << -12 << endl; cout << oct << setw(4) << 12 << setw(12) << -12 << endl; return 0; } |
结果:
C FFFFFFF4
+12 -12
14 37777777764
c fffffff4
12 -12
14 37777777764
利用<iomanip>的setbase函数同样可以设置整数的三种进制,参数分别为8、10和16,但使用起来比上面的方法还更复杂一些,除非是特殊的代码规范要求(有些规范要求避免将常量直接作为表达式),一般不建议使用setbase。此外,还可以利用ios::showbase来为整数的前面加一个表示进制的前缀,代码如下:
1
2
3
4
5
6
7
8
|
#include <iomanip> #include <iostream> using namespace std; int main( void ) { cout << showbase << setw(4) << hex << 32 << setw(4) << oct << 32 << endl; cout << noshowbase << setw(4) << hex << 32 << setw(4) << oct << 32 << endl; return 0; } |
结果:
0x20 040
20 40
上面代码中的showbase/noshobase也可以用cout的setf来代替,其结果是完全相同的:
1
2
3
4
5
6
7
8
9
10
|
#include <iomanip> #include <iostream> using namespace std; int main( void ) { cout.setf(ios::showbase); cout << setw(4) << hex << 32 << setw(4) << oct << 32 << endl; cout.unsetf(ios::showbase); cout << setw(4) << hex << 32 << setw(4) << oct << 32 << endl; return 0; } |
四、小数
小数可分为两种格式类型,一种是定点表示“ios::fixed”(不带指数域),另一种是科学计数法表示“ios::scientific”(带指数域)。与<iomanip>的setprecision配合使用,可以表示指定小数点后面的保留位数(四舍五入)。示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <iomanip> #include <iostream> using namespace std; int main( void ) { cout.setf(ios::fixed); cout << setprecision(0) << 12.05 << endl; cout << setprecision(1) << 12.05 << endl; cout << setprecision(2) << 12.05 << endl; cout << setprecision(3) << 12.05 << endl; cout.setf(ios::scientific, ios::floatfield); cout << setprecision(0) << 12.05 << endl; cout << setprecision(1) << 12.05 << endl; cout << setprecision(2) << 12.05 << endl; cout << setprecision(3) << 12.05 << endl; return 0; } |
结果:
12
12.1
12.05
12.050
1.205000e+001
1.2e+001
1.21e+001
1.205e+001
需要注意的是,有时会因为机器的精度问题导致四舍五入的结果不正确。这种问题一般需要手动修正,见如下代码示例:
1
2
3
4
5
6
7
8
|
#include <iomanip> #include <iostream> using namespace std; int main( void ) { cout << fixed << setprecision(1) << 2.05 << endl; cout << fixed << setprecision(1) << 2.05 + 1e-8 << endl; return 0; } |
结果:
2.0
2.1
四、字符串
字符串的输出处理主要是对齐,这一点在第二部分已经介绍过了,下面主要介绍字符串的输入方法。为了方便起见,我们使用<string>库。在输入字符串时,可以利用<string>库提供的getline函数读取整行数据。getline函数有两个版本,第一个版本有两个参数,第一个参数指定输入流(比如cin),第二个参数指定一个string对象。getline会读取屏幕上输入的字符,直到遇到换行符“\n”为止;第二个版本有三个参数,前两个与第一个版本相同,第三个参数为指定的结束字符。注意,getline不会读入默认或指定的结束字符,但在调用之后读取的位置已经跳过结束字符。调用示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
|
#include <iomanip> #include <iostream> #include <string> using namespace std; int main( void ) { string str1, str2; getline(cin, str1); cin >> str2; cout << str1 << endl << str2 << endl; return 0; } |
输入:
abc
abc
结果:
abc
abc
五、缓冲区
由于调用系统函数在屏幕上逐个显示字符是很慢的,因此cin/cout为了加快速度使用缓冲区技术,粗略的讲就是暂时不输出指定的字符,而是存放在缓冲区中,在合适的时机一次性输出到屏幕上。如果单纯使用C++的输入/输出流来操作字符是不存在同步的问题的,但是如果要和C标准库的stdio库函数混合使用就必须要小心的处理缓冲区了。如果要与scanf和printf联合使用,务必在调用cout前加上cout.sync_with_stdio(),设置与stdio同步,否则输出的数据顺序会发生混乱。
flush和endl都会将当前缓冲区中的内容立即写入到屏幕上,而unitbuf/nounitbuf可以禁止或启用缓冲区。示例代码如下:
1
2
3
4
5
6
7
8
|
#include <iomanip> #include <iostream> using namespace std; int main( void ) { cout << 123 << flush << 456 << endl; cout << unitbuf << 123 << nounitbuf << 456 << endl; return 0; } |
结果:
123456
123456
六、综合使用
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <iomanip> #include <iostream> #include <string> using namespace std; struct COMMODITY { string Name; int Id; int Cnt; double Price; }; int main( void ) { COMMODITY cmd[] = { { "Fruit" , 0x101, 50, 5.268}, { "Juice" , 0x102, 20, 8.729}, { "Meat" , 0x104, 30, 10.133}, }; cout << left << setw(8) << "NAME" << right << setw(8) << "ID" ; cout << right << setw(8) << "COUNT" << right << setw(8) << "PRICE" << endl; for ( int i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i) { cout << left << setw(8) << cmd[i].Name; cout << right << hex << showbase << setw(8) << cmd[i].Id; cout << dec << noshowbase << setw(8) << cmd[i].Cnt; cout << fixed << setw(8) << setprecision(2) << cmd[i].Price << endl; } return 0; } |
结果:
NAME ID COUNT PRICE
Fruit 0x101 50 5.27
Juice 0x102 20 8.73
Meat 0x104 30 10.13
C++的cout高阶格式化操作的更多相关文章
- c#语言-高阶函数
介绍 如果说函数是程序中的基本模块,代码段,那高阶函数就是函数的高阶(级)版本,其基本定义如下: 函数自身接受一个或多个函数作为输入. 函数自身能输出一个函数,即函数生产函数. 满足其中一个条件就可以 ...
- swift 的高阶函数的使用代码
//: Playground - noun: a place where people can play import UIKit var str = "Hello, playground& ...
- JavaScript高阶函数
所谓高阶函数(higher-order function) 就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数. 下面的例子接收两个函数f()和g(),并返回一个新的函数用以计算f(g ...
- 分享录制的正则表达式入门、高阶以及使用 .NET 实现网络爬虫视频教程
我发布的「正则表达式入门以及高阶教程」,欢迎学习. 课程简介 正则表达式是软件开发必须掌握的一门语言,掌握后才能很好地理解到它的威力: 课程采用概念和实验操作 4/6 分隔,帮助大家理解概念后再使用大 ...
- python--函数式编程 (高阶函数(map , reduce ,filter,sorted),匿名函数(lambda))
1.1函数式编程 面向过程编程:我们通过把大段代码拆成函数,通过一层一层的函数,可以把复杂的任务分解成简单的任务,这种一步一步的分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计的基本单元. ...
- python学习道路(day4note)(函数,形参实参位置参数匿名参数,匿名函数,高阶函数,镶嵌函数)
1.函数 2种编程方法 关键词面向对象:华山派 --->> 类----->class面向过程:少林派 -->> 过程--->def 函数式编程:逍遥派 --> ...
- Scala的函数,高阶函数,隐式转换
1.介绍 2.函数值复制给变量 3.案例 在前面的博客中,可以看到这个案例,关于函数的讲解的位置,缺省. 4.简单的匿名函数 5.将函数做为参数传递给另一个函数 6.函数作为输出值 7.类型推断 8. ...
- Python之路 day3 高阶函数
#!/usr/bin/env python # -*- coding:utf-8 -*- #Author:ersa """ 变量可以指向函数,函数的参数能接收变量, 那么 ...
- JavaScript高阶函数 map reduce filter sort
本文是笔者在看廖雪峰老师JavaScript教程时的个人总结 高阶函数 一个函数就接收另一个函数作为参数,这种函数就称之为高阶函数 1.高阶函数之map: ...
随机推荐
- 07 mongodb
mongodb mongodb简介 简介 MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为Web应用提供可扩展的高性能数据存储解决方案. MongoDB是一个介于关系数据 ...
- 实验:iscsi共享存储
实验名称: iscsi共享存储 实验环境: 我们需要准备一个磁盘,对于这个磁盘我们需要使用,将这个磁盘空间共享给iscsi客户端: 实验需求: 我们这里使用两台服务器来实现iscsi共享存储: 1.指 ...
- cf837d Round Subset
设dp[i][j][k]表示前i个数中选j个并且因子含有k个2的能获得的最多的5的个数 则dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-1][k-cnt2]+cnt5 ...
- 安装SecureCRT注册
注册方法 1.首先运行压缩包中的安装程序进行安装原版程序!2.安装完成后记得先不要运行程序!3.复制压缩包中的keygen.exe程序到安装目录!4.运行keygen.exe,点击Patch按钮对源程 ...
- Flask 架构 --xunfeng实例研究
文件结构 │ Config.py # 配置文件 │ README.md # 说明文档 │ Run.bat # Windows启动服务 │ Run.py # webserver │ Run.sh # L ...
- [luoguP3606] [USACO17JAN]Building a Tall Barn建谷仓(贪心 + 线段树)
传送门 把线段都读进来然后排序,先按右端点为第一关键字从小到大排序,后按左端点为第二关键字从小到大排序. 注意不能先按左端点后按右端点排序,否则会出现大包小的情况,如下: —————— ——— — ...
- hdu 2579
#include<stdio.h> #include<queue> #include<iostream> #include<string.h> #inc ...
- [NOIP2000] 提高组 洛谷P1022 计算器的改良
题目背景 NCL是一家专门从事计算器改良与升级的实验室,最近该实验室收到了某公司所委托的一个任务:需要在该公司某型号的计算器上加上解一元一次方程的功能.实验室将这个任务交给了一个刚进入的新手ZL先生. ...
- js1:对象的学习,构造函数,继承构造函数【使用教材:JavaScript深度剖析第2版】
原文发布时间为:2008-11-08 -- 来源于本人的百度文章 [由搬家工具导入] <html> <head> <title>js</title> & ...
- php之ThinkPHP的memcached类的修改
php之ThinkPHP的memcached类的修改 在Think\Cache\Driver\Memcached.class.php中,增加方法获取错误信息的方法,方便调试, public funct ...