C++17 STL Cook Book
  • Introduction
  • 前言
  • 关于本书
  • 各章梗概
  • 第1章 C++17的新特性
    • 使用结构化绑定来解包绑定的返回值
    • 将变量作用域限制在if和switch区域内
    • 新的括号初始化规则
    • 构造函数自动推导模板的类型
    • 使用constexpr-if简化编译
    • 只有头文件的库中启用内联变量
    • 使用折叠表达式实现辅助函数
  • 第2章 STL容器
    • 擦除/移除std::vector元素
    • 以O(1)的时间复杂度删除未排序std::vector中的元素
    • 快速或安全的访问std::vector实例的方法
    • 保持对std::vector实例的排序
    • 向std::map实例中高效并有条件的插入元素
    • 了解std::map::insert新的插入提示语义
    • 高效的修改std::map元素的键值
    • std::unordered_map中使用自定义类型
    • 过滤用户的重复输入,并以字母序将重复信息打印出——std::set
    • 实现简单的逆波兰表示法计算器——std::stack
    • 实现词频计数器——std::map
    • 实现写作风格助手用来查找文本中很长的句子——std::multimap
    • 实现个人待办事项列表——std::priority_queue
  • 第3章 迭代器
    • 建立可迭代区域
    • 让自己的迭代器与STL的迭代器兼容
    • 使用迭代适配器填充通用数据结构
    • 使用迭代器实现算法
    • 使用反向迭代适配器进行迭代
    • 使用哨兵终止迭代
    • 使用检查过的迭代器自动化检查迭代器代码
    • 构建zip迭代适配器
  • 第4章 Lambda表达式
    • 使用Lambda表达式定义函数
    • 使用Lambda为std::function添加多态性
    • 并置函数
    • 通过逻辑连接创建复杂谓词
    • 使用同一输入调用多个函数
    • 使用std::accumulate和Lambda函数实现transform_if
    • 编译时生成笛卡尔乘积
  • 第5章 STL基础算法
    • 容器间相互复制元素
    • 容器元素排序
    • 从容器中删除指定元素
    • 改变容器内容
    • 在有序和无序的vector中查找元素
    • 将vector中的值控制在特定数值范围内——std::clamp
    • 在字符串中定位模式并选择最佳实现——std::search
    • 对大vector进行采样
    • 生成输入序列的序列
    • 实现字典合并工具
  • 第6章 STL算法的高级使用方式
    • 使用STL算法实现单词查找树类
    • 使用树实现搜索输入建议生成器
    • 使用STL数值算法实现傅里叶变换
    • 计算两个vector的误差和
    • 使用ASCII字符曼德尔布罗特集合
    • 实现分割算法
    • 将标准算法进行组合
    • 删除词组间连续的空格
    • 压缩和解压缩字符串
  • 第7章 字符串, 流和正则表达
    • 创建、连接和转换字符串
    • 消除字符串开始和结束处的空格
    • 无需构造获取std::string
    • 从用户的输入读取数值
    • 计算文件中的单词数量
    • 格式化输出
    • 使用输入文件初始化复杂对象
    • 迭代器填充容器——std::istream
    • 迭代器进行打印——std::ostream
    • 使用特定代码段将输出重定向到文件
    • 通过集成std::char_traits创建自定义字符串类
    • 使用正则表达式库标记输入
    • 简单打印不同格式的数字
    • 从std::iostream错误中获取可读异常
  • 第8章 工具类
    • 转换不同的时间单位——std::ratio
    • 转换绝对时间和相对时间——std::chrono
    • 安全的标识失败——std::optional
    • 对元组使用函数
    • 使用元组快速构成数据结构
    • 将void*替换为更为安全的std::any
    • 存储不同的类型——std::variant
    • 自动化管理资源——std::unique_ptr
    • 处理共享堆内存——std::shared_ptr
    • 对共享对象使用弱指针
    • 使用智能指针简化处理遗留API
    • 共享同一对象的不同成员
    • 选择合适的引擎生成随机数
    • 让STL以指定分布方式产生随机数
  • 第9章 并行和并发
    • 标准算法的自动并行
    • 让程序在特定时间休眠
    • 启动和停止线程
    • 打造异常安全的共享锁——std::unique_lock和std::shared_lock
    • 避免死锁——std::scoped_lock
    • 同步并行中使用std::cout
    • 进行延迟初始化——std::call_once
    • 将执行的程序推到后台——std::async
    • 实现生产者/消费者模型——std::condition_variable
    • 实现多生产者/多消费者模型——std::condition_variable
    • 并行ASCII曼德尔布罗特渲染器——std::async
    • 实现一个小型自动化并行库——std::future
  • 第10章 文件系统
    • 实现标准化路径
    • 使用相对路径获取规范的文件路径
    • 列出目录下的所有文件
    • 实现一个类似grep的文本搜索工具
    • 实现一个自动文件重命名器
    • 实现一个磁盘使用统计器
    • 计算文件类型的统计信息
    • 实现一个工具:通过符号链接减少重复文件,从而控制文件夹大小
Powered by GitBook
On this page
  • How to do it...
  • How it works...

Was this helpful?

  1. 第7章 字符串, 流和正则表达

格式化输出

很多情况下,仅打印字符串和数字是不够的。数字通常都以十进制进行打印,有时我们需要使用十六进制或八进制进行打印。并且在打印十六进制的时候,我们希望看到以0x为前缀的十六进制的数字,但有时却不希望看到这个前缀。

当对浮点数进行打印的时候,也需要注意很多。以何种精度进行打印?要将数中的所有内容进行打印吗?或者是如何打印科学计数法样式的数?

除了数值表示方面的问题外,还需要规范我们打印的格式。有时我们要以表格的方式进行打印,以确保打印数据的可读性。

这所有的一切都与输出流有关,对输入流的解析也十分重要。本节中,我们将来感受一下格式化输出。有些显示也会比较麻烦,不过我们会对其进行解释。

How to do it...

为了让大家熟悉格式化输出,本节我们将使用各种各样的格式进行打印:

  1. 包含必要的头文件,并声明所使用的命名空间:

    #include <iostream>
    #include <iomanip>
    #include <locale>
    
    using namespace std;
  2. 接下来,定义一个辅助函数,其会以不同的方式打印出一个数值。其能接受使用一种字符对宽度进行填充,其默认字符为空格:

    void print_aligned_demo(int val,
                            size_t width,
                            char fill_char = ' ')
    {
  3. 使用setw,我们可以设置打印数字的最小字符数输出个数。当我们要将123的输出宽度设置为6时,我们会得到"abc "或" abc"。我们也可以使用std::left, std::right和std::internal控制从哪边进行填充。当我们以十进制的方式对数字进行输出,internal看起来和right的作用一样。不过,当打印0x1时,打印宽度为6时,internal会得到"0x 6"。setfill控制符可以用来定义填充字符。我么可以尝试使用使用以下方式进行打印:

        cout << "================\n";
        cout << setfill(fill_char);
        cout << left << setw(width) << val << '\n';
        cout << right << setw(width) << val << '\n';
        cout << internal << setw(width) << val << '\n';
    }
  4. 主函数中,我们使用已经实现的函数。首先,打印数字12345,其宽度为15。我们进行两次打印,不过第二次时,将填充字符设置为'_':

    int main()
    {
        print_aligned_demo(123456, 15);
        print_aligned_demo(123456, 15, '_');
  5. 随后,我们将打印0x123abc,并使用同样的宽度。不过,打印之前需要使用的是std::hex和std::showbase告诉输出流对象cout输出的格式,并且添加0x前缀,看起来是一个十六进制数:

        cout << hex << showbase;
        print_aligned_demo(0x123abc, 15);
  6. 对于八进制我们也可以做同样的事:

        cout << oct;
        print_aligned_demo(0123456, 15);
  7. 通过hex和uppercase,我们可以将0x中的x转换成大写字母。0x123abc中的abc同样也转换成大写:

        cout << "A hex number with upper case letters: "
            << hex << uppercase << 0x123abc << '\n';
  8. 如果我们要以十进制打印100,我们需要将输出从hex切换回dec:

        cout << "A number: " << 100 << '\n';
        cout << dec;
    
        cout << "Oops. now in decimal again: " << 100 << '\n';
  9. 我们可以对布尔值的输出进行配置,通常,true会打印出1,false为0。使用boolalpha,我们就可以得到文本表达:

        cout << "true/false values: "
            << true << ", " << false << '\n';
        cout << boolalpha
            << "true/false values: "
            << true << ", " << false << '\n';
  10. 现在让我们来一下浮点型变量float和double的打印。当我们有一个数12.3,那么打印也应该是12.3。当我们有一个数12.0,打印时会将小数点那一位进行丢弃,不过我们可以通过showpoint来控制打印的精度。使用这个控制符,就能显示被丢弃的一位小数了:

       cout << "doubles: "
           << 12.3 << ", "
           << 12.0 << ", "
           << showpoint << 12.0 << '\n';
  11. 可以使用科学计数法或固定浮点的方式来表示浮点数。scientific会将浮点数归一化成一个十进制的小数,并且其后面的位数使用10的幂级数表示,其需要进行乘法后才能还原成原始的浮点数。比如,300.0科学计数法就表示为"3.0E2",因为300 = 3.0 x $10^2$。fixed将会恢复普通小数的表达方式:

        cout << "scientific double: " << scientific
            << 123000000000.123 << '\n';
        cout << "fixed double: " << fixed
            << 123000000000.123 << '\n';
  12. 除此之外,我们也能对打印的精度进行控制。我们先创建一个特别小的浮点数,并对其小数点后的位数进行控制:

        cout << "Very precise double: "
            << setprecision(10) << 0.0000000001 << '\n';
        cout << "Less precise double: "
            << setprecision(1) << 0.0000000001 << '\n';
    }
  13. 编译并运行程序,我们就会得到如下的输出。前四个块都是有打印辅助函数完成,其使用setw对字符串进行了不同方向的填充。此外,我们也进行了数字的进制转换、布尔数表示和浮点数表示。通过实际操作,我们会对其更加熟悉:

    $ ./formatting
    =====================
    123456         
             123456
             123456
    =====================
    123456_________
    _________123456
    _________123456
    =====================
    0x123abc       
           0x123abc
    0x       123abc
    =====================
    0123456        
            0123456
            0123456
    A hex number with upper case letters: 0X123ABC
    A number: 0X64
    Ooop. now in decimal again: 100
    true/false values: 1, 0
    true/false values: true, false
    doubles: 12.3, 12, 12.0000
    scientific double: 1.230000E+12
    fixed double: 1230000000000.123047
    Very precise double: 0.0000000001
    Less previse double: 0.0

How it works...

例程看起来有些长,并且<< foo << bar的方式对于初级读者来说会感觉到困惑。因此,让我们来看一下格式化修饰符的表。其都是用input_stream >> modifier或output_stream << modifier来对之后的输入输出进行影响:

符号

描述

打印浮点数时,决定打印小数点后的位数。

启用或禁用浮点数字小数点的打印,即使没有小数位。

数字可以以固定格式和科学表达式的方式进行打印。fixed和scientific代表了相应的打印模式。hexfloat将会同时激活这两种模式,用十六进制浮点表示法格式化浮点数。defaultfloat则会禁用这两种模式。

启用或禁用使用'+'来标志正浮点数。

设置打印的宽度n。在读取的时候,这种设置会截断输入。当打印位数不够时,其会使用填充字符将输出填充到n个字符。

当我们setw时,会涉及填充字符的设置。setfill可以将填充字符设置为c。其默认填充字符为空格。

left和right控制填充的方向。internal会将填充字符放置在数字和符号之间,这对于十六进制打印和一些金融数字来说,十分有用。

整数打印的类型,十进制、十六进制和八进制。

数字类型的同义函数,当n为10/16/8时,与dec / hex / oct完全相同。当传入0时,则会恢复默认输出,也就是十进制,或者使用数字的前缀对输入进行解析。

将带有引号的字符串的引号去掉,对其实际字符进行打印。这里string的类型可以是string类的实例,也可以是一个C风格的字符串。

打印布尔变量,是打印字符形式的,还是数字形式的。

启用或禁用基于前缀的数字解析。对于hex来说就是0x,对于octal来说就是0。

启用或禁用将浮点数中的字母或十六进制中的字符进行大写输出。

看起来很多,想要熟悉这些控制符的最好方式,还是尽可能多的使用它们。

在使用中会发现,其中有一些控制符具有粘性,另一些没有。这里的粘性是说其会持续影响接下来的所有输入或输出,直到对控制符进行重置。表格中没有粘性的为setw和quoted控制符。其只对下一次输入或输入有影响。了解这些非常重要,当我们要持续使用一个格式进行打印时,对于有粘性的控制符我们设置一次即可,其余的则在需要是进行设置。这些对输入解析同样适用,不过错误的设置了控制符则会得到错误的输入信息。

下面的一些控制符我们没有使用它们,因为他们对于格式化没有任何影响,但出于完整性的考量我们在这里也将这些流状态控制符列出来:

符号

描述

启用或禁用输入流对空格进行略过的特性。

启用或禁用在进行任何输出操作后,就立即对输出缓存进行刷新。

从输入流舍弃前导空格。

向流中输入一个终止符\0。

对输出缓存区进行刷新。

向输出流中插入\n字符,并且刷新输出缓存区。

这些控制符中,只有skipws / noskipws和unitbuf / nounitbuf是具有粘性的。

Previous计算文件中的单词数量Next使用输入文件初始化复杂对象

Last updated 6 years ago

Was this helpful?

setprecision(int)
showpoint / noshowpoint
fixed /scientific / hexfloat /defaultfloat
showpos / noshowpos
setw(int n)
setfill(char c)
internal / left / right
dec / hex / oct
setbase(int n)
quoted(string)
boolalpha / noboolalpha
showbase / noshowbase
uppercase / nouppercase
skipws / noskipws
unitbuf / nounitbuf
ws
ends
flush
endl