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. cout.setf(ios::right | ios::hex); //设置16进制右对齐
  2. 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. #include <iomanip>
  2. #include <iostream>

二、缩进

将输出内容按指定的宽度对齐,需要用到ios::right、ios::left、ios::internal和iomanip里的setw。其中setw用于指定要输出内容的对齐宽度。以下两段代码的结果完全相同,前面是一个浮点数-456.98,后面紧跟着一个字符串“The End”以及换行符“endl”。

代码一:

  1. #include <iomanip>
  2. #include <iostream>
  3. using namespace std;
  4. int main(void) {
  5. cout.flags(ios::left); //左对齐
  6. cout << setw() << -456.98 << "The End" << endl;
  7. cout.flags(ios::internal); //两端对齐
  8. cout << setw() << -456.98 << "The End" << endl;
  9. cout.flags(ios::right); //右对齐
  10. cout << setw() << -456.98 << "The End" << endl;
  11. return ;
  12. }

代码二:

  1. #include <iomanip>
  2. #include <iostream>
  3. using namespace std;
  4. int main(void) {
  5. cout << left << setw() << -456.98 << "The End" << endl; //左对齐
  6. cout << internal << setw() << -456.98 << "The End" << endl; //两端对齐
  7. cout << right << setw() << -456.98 << "The End" << endl; //右对齐
  8. return ;
  9. }

结果:

  1. -456.98 The End
  2. - 456.98The End
  3. -456.98The End

这里要额外说明的一点是,setw函数会用当前的填充字符控制对齐位置,默认的填充字符是空格。可以通过<iomanip>的setfill来设置填充字符,比如下面的代码用字符“0”作为填充字符:

  1. #include <iomanip>
  2. #include <iostream>
  3. using namespace std;
  4. int main(void) {
  5. cout << setfill('') << setw() << << endl;
  6. return ;
  7. }

结果:

0000045698

三、整数

输出整数的格式有按不同进制数出:ios::hex(16进制)、ios::dec(10进制)、ios::oct(8进制),也可强制其输出符号(正数也加上“+”号前缀),对于16进制的输出还可配合ios::uppercase使所有字母以大写表示。代码示例如下:

  1. #include <iomanip>
  2. #include <iostream>
  3. using namespace std;
  4. int main(void) {
  5. cout.setf(ios::showpos | ios::uppercase);
  6. cout << hex << setw() << << setw() << - << endl;
  7. cout << dec << setw() << << setw() << - << endl;
  8. cout << oct << setw() << << setw() << - << endl;
  9. cout.unsetf(ios::showpos | ios::uppercase);
  10. cout << hex << setw() << << setw() << - << endl;
  11. cout << dec << setw() << << setw() << - << endl;
  12. cout << oct << setw() << << setw() << - << endl;
  13. return ;
  14. }

结果:

  1. C FFFFFFF4
  2. +12 -12
  3. 14 37777777764
  4. c fffffff4
  5. 12 -12
  6. 14 37777777764

利用<iomanip>的setbase函数同样可以设置整数的三种进制,参数分别为8、10和16,但使用起来比上面的方法还更复杂一些,除非是特殊的代码规范要求(有些规范要求避免将常量直接作为表达式),一般不建议使用setbase。此外,还可以利用ios::showbase来为整数的前面加一个表示进制的前缀,代码如下:

  1. #include <iomanip>
  2. #include <iostream>
  3. using namespace std;
  4. int main(void) {
  5. cout << showbase << setw() << hex << << setw() << oct << << endl;
  6. cout << noshowbase << setw() << hex << << setw() << oct << << endl;
  7. return ;
  8. }

结果:

  1. 0x20 040
  2. 20 40

上面代码中的showbase/noshobase也可以用cout的setf来代替,其结果是完全相同的:

  1. #include <iomanip>
  2. #include <iostream>
  3. using namespace std;
  4. int main(void) {
  5. cout.setf(ios::showbase);
  6. cout << setw() << hex << << setw() << oct << << endl;
  7. cout.unsetf(ios::showbase);
  8. cout << setw() << hex << << setw() << oct << << endl;
  9. return ;
  10. }

四、小数

小数可分为两种格式类型,一种是定点表示“ios::fixed”(不带指数域),另一种是科学计数法表示“ios::scientific”(带指数域)。与<iomanip>的setprecision配合使用,可以表示指定小数点后面的保留位数(四舍五入)。示例代码如下:

  1. #include <iomanip>
  2. #include <iostream>
  3. using namespace std;
  4. int main(void) {
  5. cout.setf(ios::fixed);
  6. cout << setprecision() << 12.05 << endl;
  7. cout << setprecision() << 12.05 << endl;
  8. cout << setprecision() << 12.05 << endl;
  9. cout << setprecision() << 12.05 << endl;
  10. cout.setf(ios::scientific, ios::floatfield);
  11. cout << setprecision() << 12.05 << endl;
  12. cout << setprecision() << 12.05 << endl;
  13. cout << setprecision() << 12.05 << endl;
  14. cout << setprecision() << 12.05 << endl;
  15. return ;
  16. }

结果:

  1. 12
  2. 12.1
  3. 12.05
  4. 12.050
  5. 1.205000e+001
  6. 1.2e+001
  7. 1.21e+001
  8. 1.205e+001

需要注意的是,有时会因为机器的精度问题导致四舍五入的结果不正确。这种问题一般需要手动修正,见如下代码示例:

  1. #include <iomanip>
  2. #include <iostream>
  3. using namespace std;
  4. int main(void) {
  5. cout << fixed << setprecision() << 2.05 << endl;
  6. cout << fixed << setprecision() << 2.05 + 1e- << endl;
  7. return ;
  8. }

结果:

  1. 2.0
  2. 2.1

五、字符串

字符串的输出处理主要是对齐,这一点在第二部分已经介绍过了,下面主要介绍字符串的输入方法。为了方便起见,我们使用<string>库。在输入字符串时,可以利用<string>库提供的getline函数读取整行数据。getline函数有两个版本,第一个版本有两个参数,第一个参数指定输入流(比如cin),第二个参数指定一个string对象。getline会读取屏幕上输入的字符,直到遇到换行符“\n”为止;第二个版本有三个参数,前两个与第一个版本相同,第三个参数为指定的结束字符。注意,getline不会读入默认或指定的结束字符,但在调用之后读取的位置已经跳过结束字符。调用示例代码如下:

  1. #include <iomanip>
  2. #include <iostream>
  3. #include <string>
  4. using namespace std;
  5. int main(void) {
  6. string str1, str2;
  7. getline(cin, str1);
  8. cin >> str2;
  9. cout << str1 << endl << str2 << endl;
  10. return ;
  11. }

输入:

  1. abc
  2. abc

结果:

  1. abc
  2. abc

六、缓冲区

由于调用系统函数在屏幕上逐个显示字符是很慢的,因此cin/cout为了加快速度使用缓冲区技术,粗略的讲就是暂时不输出指定的字符,而是存放在缓冲区中,在合适的时机一次性输出到屏幕上。如果单纯使用C++的输入/输出流来操作字符是不存在同步的问题的,但是如果要和C标准库的stdio库函数混合使用就必须要小心的处理缓冲区了。如果要与scanf和printf联合使用,务必在调用cout前加上cout.sync_with_stdio(),设置与stdio同步,否则输出的数据顺序会发生混乱。

flush和endl都会将当前缓冲区中的内容立即写入到屏幕上,而unitbuf/nounitbuf可以禁止或启用缓冲区。示例代码如下:

  1. #include <iomanip>
  2. #include <iostream>
  3. using namespace std;
  4. int main(void) {
  5. cout << << flush << << endl;
  6. cout << unitbuf << << nounitbuf << << endl;
  7. return ;
  8. }

结果:

  1. 123456
  2. 123456

七、综合使用

示例代码:

  1. #include <iomanip>
  2. #include <iostream>
  3. #include <string>
  4. using namespace std;
  5. struct COMMODITY { string Name; int Id; int Cnt; double Price; };
  6. int main(void) {
  7. COMMODITY cmd[] = {
  8. {"Fruit", 0x101, , 5.268},
  9. {"Juice", 0x102, , 8.729},
  10. {"Meat", 0x104, , 10.133},
  11. };
  12. cout << left << setw() << "NAME" << right << setw() << "ID";
  13. cout << right << setw() << "COUNT" << right << setw() << "PRICE" << endl;
  14. for (int i = ; i < sizeof(cmd) / sizeof(cmd[]); ++i) {
  15. cout << left << setw() << cmd[i].Name;
  16. cout << right << hex << showbase << setw() << cmd[i].Id;
  17. cout << dec << noshowbase << setw() << cmd[i].Cnt;
  18. cout << fixed << setw() << setprecision() << cmd[i].Price << endl;
  19. }
  20. return ;
  21. }

结果:

  1. NAME ID COUNT PRICE
  2. Fruit 0x101 50 5.27
  3. Juice 0x102 20 8.73
  4. Meat 0x104 30 10.13

C++ cout格式化输出(转)的更多相关文章

  1. C++ cout格式化输出完全攻略

    写算法题的时候突然发现自己忘记基本的C++:cout格式化输出了,赶紧拉出以前的C++学习笔记重新看一看. 部分内容来自教程:C语言中文网(一个很棒的网站) 有时希望按照一定的格式进行输出,如按十六进 ...

  2. cout 格式化输出

    一直习惯于C语言的printf函数来打印,突然有一天要用cout来打印,发现有点不适应. 原来cout也是有格式化输出的. 首先要引入头文件 #include<iostream> // 在 ...

  3. [转载] c++ cout 格式化输出浮点数、整数及格方法

    C语言里可以用printf(),%f来实现浮点数的格式化输出,用cout呢...? 下面的方法是在网上找到的,如果各位有别的办法谢谢留下... iomanip.h是I/O流控制头文件,就像C里面的格式 ...

  4. [ZZ]c++ cout 格式化输出浮点数、整数及格式化方法

    C语言里可以用printf(),%f来实现浮点数的格式化输出,用cout呢...?下面的方法是在网上找到的,如果各位有别的办法谢谢留下... iomanip.h是I/O流控制头文件,就像C里面的格式化 ...

  5. C++ cout 格式化输出方法

    C语言里可以用printf(),%f来实现浮点数的格式化输出,用cout呢...? iomanip是I/O流控制头文件,就像printf的格式化输出一样. 以下是一些常用的: dec 置基数为10 相 ...

  6. cout格式化输出

    问题描述: 有N条绳子, 它们的长度分别为Li. 如果从它们中切割出K条相同的绳子的话,这K条绳子每条最长能有多少? (备注:答案保留两位小数) <1>精确到小数点后两位输出 #inclu ...

  7. C++ cout格式化输出

    表1:C++ 流操纵算子 流操纵算子 作  用 *dec 以十进制形式输出整数 常用 hex 以十六进制形式输出整数 oct 以八进制形式输出整数 fixed 以普通小数形式输出浮点数 scienti ...

  8. cin与cout格式化输出

    cin 中“<<”流 输入运算符 ----------------------------------------------------------------------------- ...

  9. cout格式化输出 详解

    //在使用setf等库函数时使用 //在使用流操纵算子时使用 //using namespace std; //以下所有的setf()都有对应的unsetf()用于取消设置 //所有的setiosfl ...

随机推荐

  1. 如何下载旧版本的MySQL

    可能存在这样的场景,比如一些老系统需要使用MySQL 5.5版本才能运行,其余的不行. 1.登录下载站点 https://dev.mysql.com/downloads/mysql/ 此时的最新版本为 ...

  2. HTML5-Input

    HTML5拥有多个新的表单输入类型,这些新特性提供了更好的输入控制和验证(有的浏览器不支持) color.date.datetime.datetime-local.email.month.number ...

  3. java读取excel获取数据写入到另外一个excel

    pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="htt ...

  4. Codeforces1065F Up and Down the Tree 【树形DP】

    推荐一道联赛练习题. 题目分析: 你考虑进入一个子树就可能上不来了,如果上得来的话就把能上来的全捡完然后走一个上不来的,所以这就是个基本的DP套路. 代码: #include<bits/stdc ...

  5. Marriage Match IV HDU - 3416(最短路 + 最大流)

    题意: 求有多少条最短路 解析: 正着求一遍最短路 得dis1 反着求一遍得 dis2   然后 遍历所有的边 如果  dis1[u] + dis2[v] + w == dis1[B], 则说明这是一 ...

  6. bzoj 4196 [Noi2015]软件包管理器 (树链剖分+线段树)

    4196: [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 2852  Solved: 1668[Submit][Sta ...

  7. 常用 git 基础命令

    git config --global credential.helper store记住密码 git config user.name xxx配置帐号 git config user.email x ...

  8. 【BZOJ5322】[JXOI2018]排序问题(模拟)

    [BZOJ5322][JXOI2018]排序问题(模拟) 题面 BZOJ 洛谷 题解 这题就显得很呆. 显然就是每次找到\([l,r]\)中出现次数最小的那个数并且放一个. 然后随便模拟一下就好了Qw ...

  9. [luogu3620][APIO/CTSC 2007]数据备份【贪心+堆+链表】

    题目描述 你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份.然而数据备份的工作是枯燥乏味的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏 ...

  10. 【转】从Vue.js源码看异步更新DOM策略及nextTick

    在使用vue.js的时候,有时候因为一些特定的业务场景,不得不去操作DOM,比如这样: <template> <div> <div ref="test" ...