网易首页 > 网易号 > 正文 申请入驻

C++ 控制台格式化打印技巧 | Linux 中国

0
分享至

导读:下次当你为控制台输出的格式而苦恼时,请参考这篇文章及其速查表。                         

本文字数:8296,阅读时长大约: 9分钟

https://linux.cn/article-14027-1.html
作者:Stephan Avenwedde
译者:Xingyu.Wang

我写文章主要是为了给自己写文档。我在编程时非常健忘,所以我经常会写下有用的代码片段、特殊的特性,以及我使用的编程语言中的常见错误。这篇文章完全切合我最初的想法,因为它涵盖了从 C++ 控制台格式化打印时的常见用例。

像往常一样,这篇文章带有大量的例子。除非另有说明,代码片段中显示的所有类型和类都是 std 命名空间的一部分。所以当你阅读这段代码时,你必须在类型和类的前面加上using namespace std;。当然,该示例代码也可以在 github.com 上找到。

面向对象的流

如果你曾经用过 C++ 编程,你肯定使用过 en.cppreference.com。当你包含 时, en.cppreference.com 类型的 cout 对象就进入了作用域。这篇文章的重点是 cout,它可以让你打印到控制台,但这里描述的一般格式化对所有 en.cppreference.com 类型的流对象都有效。ostream 对象是 basic_ostream 的一个实例,其模板参数为 char 类型。头文件 是 的包含层次结构的一部分,包含了常见类型的前向声明。

basic_ostream 继承于 basic_ios,该类型又继承于 ios_base。在 en.cppreference.com 上你可以找到一个显示不同类之间关系的类图。

ios_base 类是所有 I/O 流类的基类。basic_ios 类是一个模板类,它对常见的字符类型进行了模板特化(specialization),称为 ios。因此,当你在标准 I/O 的上下文中读到 ios 时,它是 basic_ioschar 类型的模板特化。

格式化流

一般来说,基于 ostream 的流有三种格式化的方法。

1. 使用 ios_base 提供的格式标志。

2. 在头文件 和 中定义的流修改函数。

3. 通过调用 << 操作符的 en.cppreference.com。

所有这些方法都有其优点和缺点,通常取决于使用哪种方法的情况。下面显示的例子混合使用所有这些方法。

右对齐

默认情况下,cout 占用的空间与要打印的数据所需的空间一样大。为了使这种右对齐的输出生效,你必须定义一个行允许占用的最大宽度。我使用格式标志来达到这个目的。

右对齐输出的标志和宽度调整只适用于其后的行。

  1. cout.setf(ios::right, ios::adjustfield);

  2. cout.width(50);

  3. cout << "This text is right justified" << endl;

  4. cout << "This text is left justified again" << endl;

在上面的代码中,我使用 setf 配置了右对齐的输出。我建议你将位掩码 ios::adjustfield 应用于 setf,这将使位掩码指定的所有标志在实际的 ios::right 标志被设置之前被重置,以防止发生组合碰撞。

填充空白

当使用右对齐输出时,默认情况下,空的地方会用空白字符填充。你可以通过使用 setfill 指定填充字符来改变它:

  1. cout << right << setfill('.') << setw(30) << 500 << " pcs" << endl;

  2. cout << right << setfill('.') << setw(30) << 3000 << " pcs" << endl;

  3. cout << right << setfill('.') << setw(30) << 24500 << " pcs" << endl;

代码输出如下:

  1. ...........................500 pcs

  2. ..........................3000 pcs

  3. .........................24500 pcs

组合

想象一下,你的 C++ 程序记录了你的储藏室库存。不时地,你想打印一份当前库存的清单。要做到这一点,你可以使用以下格式。

下面的代码是左对齐和右对齐输出的组合,使用点作为填充字符,可以得到一个漂亮的列表:

  1. cout << left << setfill('.') << setw(20) << "Flour" << right << setfill('.') << setw(20) << 0.7 << " kg" << endl;

  2. cout << left << setfill('.') << setw(20) << "Honey" << right << setfill('.') << setw(20) << 2 << " Glasses" << endl;

  3. cout << left << setfill('.') << setw(20) << "Noodles" << right << setfill('.') << setw(20) << 800 << " g" << endl;

  4. cout << left << setfill('.') << setw(20) << "Beer" << right << setfill('.') << setw(20) << 20 << " Bottles" << endl;

输出:

  1. Flour...............................0.70 kg

  2. Honey..................................2 Glasses

  3. Noodles..............................800 g

  4. Beer..................................20 Bottles

打印数值

当然,基于流的输出也能让你输出各种变量类型。

布尔型

boolalpha 开关可以让你把布尔型的二进制解释转换为字符串:

  1. Flour...............................0.70 kg

  2. Honey..................................2 Glasses

  3. Noodles..............................800 g

  4. Beer..................................20 Bottles

以上几行产生的输出结果如下:

  1. Boolean output without using boolalpha: 1 / 0

  2. Boolean output using boolalpha: true / false

地址

如果一个整数的值应该被看作是一个地址,那么只需要把它投到 void* 就可以了,以便调用正确的重载。下面是一个例子:

  1. cout << "Boolean output without using boolalpha: " << true << " / " << false << endl;

  2. cout << "Boolean output using boolalpha: " << boolalpha << true << " / " << false << endl;

该代码产生了以下输出:

  1. Treat as unsigned long: 43981

  2. Treat as address: 0000ABCD

该代码打印出了具有正确长度的地址。一个 32 位的可执行文件产生了上述输出。

整数

打印整数是很简单的。为了演示,我使用 setfsetiosflags 来指定数字的基数。应用流修改器 hex/oct 也有同样的效果。

  1. int myInt = 123;

  2. cout << "Decimal: " << myInt << endl;

  3. cout.setf(ios::hex, ios::basefield);

  4. cout << "Hexadecimal: " << myInt << endl;

  5. cout << "Octal: " << resetiosflags(ios::basefield) << setiosflags(ios::oct) << myInt << endl;

注意: 默认情况下,没有指示所使用的基数,但你可以使用 showbase 添加一个。

  1. Decimal: 123

  2. Hexadecimal: 7b

  3. Octal: 173

用零填充

  1. 0000003

  2. 0000035

  3. 0000357

  4. 0003579

你可以通过指定宽度和填充字符得到类似上述的输出:

  1. cout << setfill('0') << setw(7) << 3 << endl;

  2. cout << setfill('0') << setw(7) << 35 << endl;

  3. cout << setfill('0') << setw(7) << 357 << endl;

  4. cout << setfill('0') << setw(7) << 3579 << endl;

浮点值

如果我想打印浮点数值,我可以选择“固定”和“科学”格式。此外,我还可以指定精度:

  1. double myFloat = 1234.123456789012345;

  2. int defaultPrecision = cout.precision(); // == 2

  3. cout << "Default precision: " << myFloat << endl;

  4. cout.precision(4);

  5. cout << "Modified precision: " << myFloat << endl;

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

  7. cout << "Modified precision & scientific format: " << myFloat << endl;

  8. /* back to default */

  9. cout.precision(defaultPrecision);

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

  11. cout << "Default precision & fixed format: " << myFloat << endl;

上面的代码产生以下输出:

  1. Default precision: 1234.12

  2. Modified precision: 1234.1235

  3. Modified precision & scientific format: 1.2341e+03

  4. Default precision & fixed format: 1234.12

时间和金钱

通过 put_money,你可以用正确的、与当地有关的格式来打印货币单位。这需要你的控制台能够输出 UTF-8 字符集。请注意,变量 specialOffering 以美分为单位存储货币价值。

  1. long double specialOffering = 9995;

  2. cout.imbue(locale("en_US.UTF-8"));

  3. cout << showbase << put_money(specialOffering) << endl;

  4. cout.imbue(locale("de_DE.UTF-8"));

  5. cout << showbase << put_money(specialOffering) << endl;

  6. cout.imbue(locale("ru_RU.UTF-8"));

  7. cout << showbase << put_money(specialOffering) << endl;

iosimbue 方法让你指定一个地区。通过命令 locale -a,你可以得到你系统中所有可用的地区标识符的列表。

  1. $99.95

  2. 99,950€

  3. 99,950₽

(不知道出于什么原因,在我的系统上,它打印的欧元和卢布有三个小数位,对我来说看起来很奇怪,但这也许是官方的格式。)

同样的原则也适用于时间输出。函数 put_time 可以让你以相应的地区格式打印时间。此外,你可以指定时间对象的哪些部分被打印出来。

  1. time_t now = time(nullptr);

  2. tm localtm = *localtime(&now);

  3. cout.imbue(locale("en_US.UTF-8"));

  4. cout << "en_US : " << put_time(&localtm, "%c") << endl;

  5. cout.imbue(locale("de_DE.UTF-8"));

  6. cout << "de_DE : " << put_time(&localtm, "%c") << endl;

  7. cout.imbue(locale("ru_RU.UTF-8"));

  8. cout << "ru_RU : " << put_time(&localtm, "%c") << endl;

格式指定符 %c 会打印一个标准的日期和时间字符串:

  1. en_US : Tue 02 Nov 2021 07:36:36 AM CET

  2. de_DE : Di 02 Nov 2021 07:36:36 CET

  3. ru_RU : Вт 02 ноя 2021 07:36:36

创建自定义的流修改器

你也可以创建你自己的流。下面的代码在应用于 ostream 对象时插入了一个预定义的字符串:

  1. ostream& myManipulator(ostream& os) {

  2. string myStr = ">>>Here I am<<<";

  3. os << myStr;

  4. return os;

  5. }

另一个例子: 如果你有重要的事情要说,就像互联网上的大多数人一样,你可以使用下面的代码在你的信息后面根据重要程度插入感叹号。重要程度被作为一个参数传递:

  1. struct T_Importance {

  2. int levelOfSignificance;

  3. };

  4. T_Importance importance(int lvl){

  5. T_Importance x = {.levelOfSignificance = lvl };

  6. return x;

  7. }

  8. ostream& operator<<(ostream& __os, T_Importance t){

  9. for(int i = 0; i < t.levelOfSignificance; ++i){

  10. __os.put('!');

  11. }

  12. return __os;

  13. }

这两个修饰符现在都可以简单地传递给 cout

  1. cout << "My custom manipulator: " << myManipulator << endl;

  2. cout << "I have something important to say" << importance(5) << endl;

产生以下输出:

  1. My custom manipulator: >>>Here I am<<<

  2. I have something important to say!!!!!

结语

下次你再纠结于控制台输出格式时,我希望你记得这篇文章及其 opensource.com。

在 C++ 应用程序中,cout 是 opensource.com 的新邻居。虽然使用 printf 仍然有效,但我可能总是喜欢使用 cout。特别是与定义在 中的修改函数相结合,会产生漂亮的、可读的代码。

via:

作者: 选题: 译者: 校对:

本文由 原创编译, 荣誉推出

欢迎遵照 CC-BY-NC-SA 协议规定转载,

如需转载,请在文章下留言 “ 转载:公众号名称”,

我们将为您添加白名单,授权“ 转载文章时可以修改”。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
法国总统马克龙凌晨发文怒斥:我的士兵被打死了,这绝不能忍!

法国总统马克龙凌晨发文怒斥:我的士兵被打死了,这绝不能忍!

小涛叨叨
2026-03-19 02:56:48
伊朗发起“真实承诺-4”行动第66波攻势

伊朗发起“真实承诺-4”行动第66波攻势

新华社
2026-03-20 07:28:02
有网民在互联网平台发布“大量特警在重庆九龙坡合信石大酒店抓人”,当地辟谣

有网民在互联网平台发布“大量特警在重庆九龙坡合信石大酒店抓人”,当地辟谣

环球网资讯
2026-03-19 13:10:17
伊朗判处3名为美以行动提供支持的人员死刑

伊朗判处3名为美以行动提供支持的人员死刑

每日经济新闻
2026-03-19 14:15:52
买莴笋时,看到这种碰都不碰,菜贩自己从不吃,别说没有提醒你

买莴笋时,看到这种碰都不碰,菜贩自己从不吃,别说没有提醒你

马蹄烫嘴说美食
2026-03-19 14:35:45
以军称过去一天打击超130个伊朗目标

以军称过去一天打击超130个伊朗目标

大象新闻
2026-03-20 07:24:04
台岛断油断气,警报拉响!国台办郑重承诺,给出能源保障

台岛断油断气,警报拉响!国台办郑重承诺,给出能源保障

云舟史策
2026-03-19 17:09:07
广州水饺店火了:一句“你工资低”,让精致穷全体沉默

广州水饺店火了:一句“你工资低”,让精致穷全体沉默

萌酱追热点
2026-03-19 13:14:29
九成人不知道,这7种蔬菜最适合冷冻,不坏还更好吃,省钱又实用

九成人不知道,这7种蔬菜最适合冷冻,不坏还更好吃,省钱又实用

江江食研社
2026-03-19 13:30:09
《好好的时光》王元义把刘成踢出局,苏小曼才知,李燕是庄家贵人

《好好的时光》王元义把刘成踢出局,苏小曼才知,李燕是庄家贵人

小兰聊历史
2026-03-19 11:32:21
周总理一生中有哪些错误?

周总理一生中有哪些错误?

顾史
2026-03-19 14:58:48
人财两空!黄山33岁汪闰昌去世,妻子堕二胎陪治,临终时不甘怒吼

人财两空!黄山33岁汪闰昌去世,妻子堕二胎陪治,临终时不甘怒吼

黑哥讲现代史
2026-03-19 15:32:54
日本陪酒女去中国旅行,因帮男性携带毒品被查获……

日本陪酒女去中国旅行,因帮男性携带毒品被查获……

日本物语
2026-03-18 22:11:10
发生大事了,美军海上霸权作废?伊朗准备收取霍尔木兹海峡通行费

发生大事了,美军海上霸权作废?伊朗准备收取霍尔木兹海峡通行费

甜柠聊史
2026-03-20 09:39:34
美伊大战还没完,中国家门口又起冲突,电话打到北京,请中国出山

美伊大战还没完,中国家门口又起冲突,电话打到北京,请中国出山

轩逸阿II
2026-03-18 00:24:41
男按摩师揭秘:很多女顾客需要的并不是按摩,更需要的是我!

男按摩师揭秘:很多女顾客需要的并不是按摩,更需要的是我!

千秋历史
2026-03-18 21:12:02
内蒙古硝化车间爆炸多名伤者已出院,58岁老员工被钢管砸中肋骨骨折:爆炸声很大,有人在二楼被冲击波震飞,食堂屋顶被炸塌

内蒙古硝化车间爆炸多名伤者已出院,58岁老员工被钢管砸中肋骨骨折:爆炸声很大,有人在二楼被冲击波震飞,食堂屋顶被炸塌

极目新闻
2026-03-19 23:46:21
Token额度算入年薪

Token额度算入年薪

新浪财经
2026-03-19 21:07:50
欧冠8强诞生,金球奖悬念已不大!将在以下7人中产生,哈兰德出局

欧冠8强诞生,金球奖悬念已不大!将在以下7人中产生,哈兰德出局

小火箭爱体育
2026-03-19 11:48:13
俄罗斯光刻机破冰!已搞定自研350nm光刻机

俄罗斯光刻机破冰!已搞定自研350nm光刻机

快科技
2026-03-19 17:02:05
2026-03-20 10:20:49
Linux
Linux
Linux 中国开源社区
8018文章数 73113关注度
往期回顾 全部

科技要闻

阿里定下新目标:云+AI剑指年收入千亿美元

头条要闻

女子控诉90万全款买房隔夜涨10万 当事人:觉得我有钱

头条要闻

女子控诉90万全款买房隔夜涨10万 当事人:觉得我有钱

体育要闻

不老神话!詹姆斯夺第4个NBA历史第1

娱乐要闻

胡歌初恋回应曝光书信 否认用爆料赚钱

财经要闻

黄金、白银、铝、铜……大宗商品巨震

汽车要闻

不到10万还有激光雷达 零跑A10体验超预期

态度原创

健康
时尚
数码
手机
军事航空

转头就晕的耳石症,能开车上班吗?

边生活,边艺术:LEDIN「智趣千金」的灵感手记

数码要闻

AMD推出26.3.1驱动,用AI榨干RX 9000系列显卡性能

手机要闻

果粉盛宴!苹果春晚一年两场:明年春季新iPhone三剑齐发

军事要闻

美国防部因打伊朗要求加2000亿预算

无障碍浏览 进入关怀版