这篇文章主要讲解如何在C++中使用cin/cout进行高级的格式化输出操作,包括数字的各种计数法(精度)输出,左或右对齐,大小写等等。通过本文,您可以完全脱离scanf/printf,仅使用cin/cout来完成一切需要的格式化输入输出功能(从非性能的角度而言)。更进一步而言,您还可以在<sstream>、<fstream>上使用这些格式化操作,从而代替sscanf/sprintf和fscanf/fprintf函数。为方便描述,下文仅以cin/cout为例进行介绍。

  一、综述

  cin/cout是STL库提供的一个iostream实例,拥有ios_base基类的全部函数和成员数据。进行格式化操作可以直接利用setf/unsetf函数和flags函数。cin/cout维护一个当前的格式状态,setf/unsetf函数是在当前的格式状态上追加或删除指定的格式,而flags则是将当前格式状态全部替换为指定的格式。cin/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”。

  代码一:

  01 #include <iomanip>

  02 #include <iostream>

  03 using namespace std;

  04 int main(void) {

  05 cout.flags(ios::left); //左对齐

  06 cout << setw(10) << -456.98 << "The End" << endl;

  07 cout.flags(ios::internal); //两端对齐

  08 cout << setw(10) << -456.98 << "The End" << endl;

  09 cout.flags(ios::right); //右对齐

  10 cout << setw(10) << -456.98 << "The End" << endl;

  11 return 0;

  12 }

  代码二:

  1 #include <iomanip>

  2 #include <iostream>

  3 using namespace std;

  4 int main(void) {

  5 cout << left << setw(10) << -456.98 << "The End" << endl; //左对齐

  6 cout << internal << setw(10) << -456.98 << "The End" << endl; //两端对齐

  7 cout << right << setw(10) << -456.98 << "The End" << endl; //右对齐

  8 return 0;

  9 }

  结果:

  -456.98 The End

  - 456.98The End

  -456.98The End

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

  1 #include <iomanip>

  2 #include <iostream>

  3 using namespace std;

  4 int main(void) {

  5 cout << setfill('0') << setw(10) << 45698 << endl;

  6 return 0;

  7 }

  结果:

  0000045698

三、整数

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

  01 #include <iomanip>

  02 #include <iostream>

  03 using namespace std;

  04 int main(void) {

  05 cout.setf(ios::showpos | ios::uppercase);

  06 cout << hex << setw(4) << 12 << setw(12) << -12 << endl;

  07 cout << dec << setw(4) << 12 << setw(12) << -12 << endl;

  08 cout << oct << setw(4) << 12 << setw(12) << -12 << endl;

  09 cout.unsetf(ios::showpos | ios::uppercase);

  10 cout << hex << setw(4) << 12 << setw(12) << -12 << endl;

  11 cout << dec << setw(4) << 12 << setw(12) << -12 << endl;

  12 cout << oct << setw(4) << 12 << setw(12) << -12 << endl;

  13 return 0;

  14 }

  结果:

  C FFFFFFF4

  +12 -12

  14 37777777764

  c fffffff4

  12 -12

  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(4) << hex << 32 << setw(4) << oct << 32 << endl;

  6 cout << noshowbase << setw(4) << hex << 32 << setw(4) << oct << 32 << endl;

  7 return 0;

  8 }

  结果:

  0x20 040

  20 40

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

  01 #include <iomanip>

  02 #include <iostream>

  03 using namespace std;

  04 int main(void) {

  05 cout.setf(ios::showbase);

  06 cout << setw(4) << hex << 32 << setw(4) << oct << 32 << endl;

  07 cout.unsetf(ios::showbase);

  08 cout << setw(4) << hex << 32 << setw(4) << oct << 32 << endl;

  09 return 0;

  10 }

  四、小数

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

  01 #include <iomanip>

  02 #include <iostream>

  03 using namespace std;

  04 int main(void) {

  05 cout.setf(ios::fixed);

  06 cout << setprecision(0) << 12.05 << endl;

  07 cout << setprecision(1) << 12.05 << endl;

  08 cout << setprecision(2) << 12.05 << endl;

  09 cout << setprecision(3) << 12.05 << endl;

  10 cout.setf(ios::scientific, ios::floatfield);

  11 cout << setprecision(0) << 12.05 << endl;

  12 cout << setprecision(1) << 12.05 << endl;

  13 cout << setprecision(2) << 12.05 << endl;

  14 cout << setprecision(3) << 12.05 << endl;

  15 return 0;

  16 }

  结果:

  12

  12.1

  12.05

  12.050

  1.205000e+001

  1.2e+001

  1.21e+001

  1.205e+001

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

  1 #include <iomanip>

  2 #include <iostream>

  3 using namespace std;

  4 int main(void) {

  5 cout << fixed << setprecision(1) << 2.05 << endl;

  6 cout << fixed << setprecision(1) << 2.05 + 1e-8 << endl;

  7 return 0;

  8 }

  结果:

  2.0

  2.1

四、字符串

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

  01 #include <iomanip>

  02 #include <iostream>

  03 #include <string>

  04 using namespace std;

  05 int main(void) {

  06 string str1, str2;

  07 getline(cin, str1);

  08 cin >> str2;

  09 cout << str1 << endl << str2 << endl;

  10 return 0;

  11 }

  输入:

  abc

  abc

  结果:

  abc

  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 << 123 << flush << 456 << endl;

  6 cout << unitbuf << 123 << nounitbuf << 456 << endl;

  7 return 0;

  8 }

  结果:

  123456

  123456

  六、综合使用

  示例代码:

  01 #include <iomanip>

  02 #include <iostream>

  03 #include <string>

  04 using namespace std;

  05 struct COMMODITY { string Name; int Id; int Cnt; double Price; };

  06 int main(void) {

  07 COMMODITY cmd[] = {

  08 {"Fruit", 0x101, 50, 5.268},

  09 {"Juice", 0x102, 20, 8.729},

  10 {"Meat", 0x104, 30, 10.133},

  11 };

  12 cout << left << setw(8) << "NAME" << right << setw(8) << "ID";

  13 cout << right << setw(8) << "COUNT" << right << setw(8) << "PRICE" << endl;

  14 for (int i = 0; i < sizeof(cmd) / sizeof(cmd[0]); ++i) {

  15 cout << left << setw(8) << cmd[i].Name;

  16 cout << right << hex << showbase << setw(8) << cmd[i].Id;

  17 cout << dec << noshowbase << setw(8) << cmd[i].Cnt;

  18 cout << fixed << setw(8) << setprecision(2) << cmd[i].Price << endl;

  19 }

  20 return 0;

  21 }

  结果:

  NAME ID COUNT PRICE

  Fruit 0x101 50 5.27

  Juice 0x102 20 8.73

  Meat 0x104 30 10.13

C++知识整理(在此感谢大牛的整理)相关推荐

  1. 小知识-为什么Linux不需要磁盘碎片整理

    ref: https://itcat.cn/post-495.html Linux不需要磁盘碎片整理的真正原因是因为Linux只是一个内核它没有磁盘可以整理. BTW,ext4已经开始提供碎片整理工具 ...

  2. 计算机二级知识汇总手抄报,以科学知识为主题的手抄报内容整理

    日常的学习生活中我们学习到了很多科学知识,下面小编为大家整理了关于科学知识的手抄报,大家一起来看看吧. 以科学知识为主题的手抄报[1] 以科学知识为主题的手抄报[2] 以科学知识为主题的手抄报[3] ...

  3. 最值得收藏的 数据结构 全部知识点思维导图整理(王道考研), 附带经典题型整理

    本文的思维导图根据王道的数据结构书本整理而来并标记出重点内容,包括了知识点和部分课后习题 思维导图源文件已经发布在我的资源当中, 点击获取全部导图和配套OneNote笔记, 有需要的可以去 我的主页 ...

  4. 04分类整理:会搜索还不够,整理、分类、归纳

    在这个当一键收藏所需信息变得越来越简单的今天,如果不去定期收纳整理.我们可以想象一下,假如我们的电脑和手机是房子,我们下载.收藏.保存的信息,资源人脉是我们每天带回来的东西,如果不定期收纳整理,住在这 ...

  5. Eslint报错整理与解决方法(持续整理)

    Eslint报错整理与解决方法(持续整理) 参考文章: (1)Eslint报错整理与解决方法(持续整理) (2)https://www.cnblogs.com/lunlunshiwo/p/859600 ...

  6. idea自动整理代码快捷键_MDK进阶使用教程,快捷启动任意软件,自动整理格式化代码,方便代码整理可以很好 的提高效率...

    MDK进阶使用教程,快捷启动任意软件,自动整理格式化代码,方便代码整理可以很好 的提高效率 以下内容才是关键 AStyle插件 这款AStyle插件挺好用的,这里也给大家推荐一下. 代码格式化插件,顾 ...

  7. 如何对计算机进行磁盘整理,WinXP电脑磁盘碎片怎么整理,教您如何给XP系统电脑进行磁盘碎片整理...

    如果电脑的运行速度开始变慢,那么就是时候对硬盘驱动器进行磁盘碎片整理了.磁盘碎片会减慢你的计算机并占用空闲的空间.因为各个磁盘会产生大量碎片,拖慢磁盘运行速度,从而导致电脑变卡,下面,小编就来教大家给 ...

  8. 固态硬盘的计算机需要进行磁盘碎片整理吗,SSD固态硬盘需要碎片整理吗 固态盘切忌磁盘碎片整理...

    SSD固态硬盘需要碎片整理吗 固态盘切忌磁盘碎片整理.对于电脑的用户来说,磁盘碎片整理可以减少硬盘冗杂文件的堆积,加速了磁盘运转速度,从而提升了电脑整体的运行速度.但是SSD千万不能碎片整理,快来看看 ...

  9. 计算机碎片整理,如何对计算机进行碎片整理

    如何整理计算机碎片 "碎片整理"是"碎片整理"的缩写,它通过组织存储在硬盘中的数据来降低碎片程度.对计算机进行碎片整理意味着对计算机的硬盘驱动器进行碎片整理.通 ...

最新文章

  1. Codeigniter 利用加密Key(密钥)的对象注入漏洞
  2. B站王道论坛计算机网络,成都理工大学计算机网络考研学长经验分享
  3. 「 每日一练,快乐水题 」258. 各位相加
  4. c++一个问题:while(!cin) 的解释
  5. Two Merged Sequences(CF 1144 G)
  6. 走进LWRP(Universal RP)的世界
  7. ASP.NET 开源导入导出库Magicodes.IE Docker中使用
  8. uniapp 开发基础环境搭建和配置
  9. linux 校园网 热点,Linux/Ubuntu 16.04 使用校园网客户端Dr.com DrClient 有线连网,同时开启WiFi热点...
  10. html切西瓜游戏源码,html5切水果源码(水果忍者)
  11. 【190111】VC+Access工程信息管理系统源代码
  12. 手眼标定原理(眼在手上和眼在手外)
  13. (解决中文标签无法显示问题)Networkx绘制《清明上河图密码》主要人物社交关系网络图
  14. iphone屏幕镜像如何全屏_iOS 11屏幕镜像无法投屏怎么办?大神教你只需三步完美解决!...
  15. 解决Can‘t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock‘ (2)
  16. 心路历程(七)-加入微医集团
  17. 如何选择自己心仪的U盘
  18. 51单片机的温湿度控制系统
  19. pojo与entity
  20. 控制windows开关机代码

热门文章

  1. 云南构建“健康医疗云框架”6项重点工程规范医疗大数据建设
  2. 【原】web服务器占有量统计等 web网站
  3. 互联网的未来之上:平权的互联网
  4. spark eventLoop模型
  5. trunc函数_同样取整,TRUNC函数与INT函数的有什么区别?
  6. map to javaBean
  7. Vue路由history模式踩坑记录:nginx配置解决404问题
  8. 使用setCustomView建立标题居中的自定义ActionBar
  9. 框架源码专题:Mybatis启动和执行流程、源码级解析
  10. Java多线程编程核心技术-多线程基础使用