五分钟c++(9)格式化标志 (Formatting flags)

相关知识请看上一篇 五分钟c++(8)iostream 标准库

在 C++ 的 iostream 库中,每一个流对象(如 cin, cout)内部都维护着一组格式化标志。这些标志本质上是一个位掩码(bitmask),其中的每一个“位”都像一个开关,控制着输入输出的某个特定方面,例如整数的进制、浮点数的表示法、对齐方式等。

你可以通过调用流对象的成员函数来设置、清除或查询这些标志。

主要的成员函数

  1. ios_base::flags():
    • fmtflags flags() const;: 返回当前所有格式标志的位掩码。
    • fmtflags flags(fmtflags fmtfl);: 用 fmtfl 替换当前所有的格式标志,并返回之前的标志。这通常用于保存旧状态,设置新状态,之后再恢复旧状态。
  2. ios_base::setf(): (set flag)
    • fmtflags setf(fmtflags fmtfl);: 打开(设置)fmtfl 中指定的标志位,不影响其他标志。返回之前的标志。
    • fmtflags setf(fmtflags fmtfl, fmtflags mask);: 先清除 mask 中指定的标志位,然后再打开 fmtfl 中指定的标志。这对于互斥的标志组(如进制、对齐方式)特别有用。
  3. ios_base::unsetf(): (unset flag)
    • void unsetf(fmtflags mask);: 关闭(清除)mask 中指定的标志位。

常见的格式化标志

这些标志定义在 std::ios_base 类中。为了方便,我们通常使用 std::ios

类别标志 (Flag)所属标志组描述
对齐leftadjustfield左对齐,在字段末尾填充
rightadjustfield右对齐(默认),在字段开头填充
internaladjustfield内部对齐,在符号或进制前缀后、数字前填充
进制decbasefield十进制(默认)
octbasefield八进制
hexbasefield十六进制
浮点数fixedfloatfield使用定点表示法(例如 123.45)
scientificfloatfield使用科学记数法(例如 1.2345e+02)
其他boolalphabool 值输出为 “true” 或 “false”
showbase输出整数时显示进制前缀(0 for oct, 0x for hex)
showpoint对于浮点数,总是显示小数点和末尾的零
showpos对于正数,显示 +
skipws(输入时) 跳过前导空白字符(默认)
unitbuf每次输出后刷新缓冲区
uppercase十六进制或科学记数法中的字母使用大写(0XFF, 1.23E+02

代码示例

https://www.programiz.com/online-compiler/8X1ZAh9WSQtet

#include <iostream>
#include <iomanip>  // 尽管这里主要用flags,但对比时会用到

int main() {
    int num = 255;
    double pi = 3.14;

    std::cout << "--- 默认输出 ---" << std::endl;
    std::cout << num << std::endl;
    std::cout << pi << std::endl;
    std::cout << true << std::endl;

    std::cout << "\n--- 使用 Formatting Flags ---" << std::endl;

    // 1. 保存当前状态
    std::ios_base::fmtflags original_flags = std::cout.flags();

    // 2. 设置新标志
    // 设置为十六进制,大写,显示进制前缀
    std::cout.setf(std::ios::hex | std::ios::uppercase | std::ios::showbase);
    std::cout << "设置 hex, uppercase, showbase: " << num << std::endl; // 输出: 0XFF

    // 清除 uppercase 标志
    std::cout.unsetf(std::ios::uppercase);
    std::cout << "清除 uppercase: " << num << std::endl; // 输出: 0xff

    // 使用 setf 的第二种形式来改变进制(这是一个互斥组)
    // 先清除 basefield 组的所有标志,再设置 dec 标志
    std::cout.setf(std::ios::dec, std::ios::basefield);
    std::cout << "切换回 dec: " << num << std::endl; // 输出: 255

    // 设置 boolalpha
    std::cout.setf(std::ios::boolalpha);
    std::cout << "设置 boolalpha: " << true << std::endl; // 输出: true

    // 3. 恢复原始状态
    std::cout.flags(original_flags);

    std::cout << "\n--- 恢复默认输出 ---" << std::endl;
    std::cout << num << std::endl;
    std::cout << true << std::endl;

    return 0;
}

这个程序会输出

--- 默认输出 ---
255
3.14
1

--- 使用 Formatting Flags ---
设置 hex, uppercase, showbase: 255
清除 uppercase: 255
切换回 dec: 255
设置 boolalpha: true

--- 恢复默认输出 ---
255
1

=== Code Execution Successful ===

什么时候用格式化标志

比起流操作符(Stream Manipulators)(这个我在下一篇介绍),如果我们想在程序的某个大部分阶段内,都遵循一种统一的格式时,在开头用 flags 设置一次是最佳选择。因为这个库的效果是持续的,所以使用Flags能让代码更加简洁和易读。

这样我们只需要在函数开始的时候设置flag,在函数结束的时候在把flag的状态还原。这样做的好处是不会在使用流的中间用操作符改状态,这样函数的意图更加明显。比如下边的例子

https://www.programiz.com/online-compiler/5bPOzgnWtxD34

// Online C++ compiler to run C++ program online
#include <iostream>
#include <vector>
#include <iomanip>

void print_data_dump(const std::vector<int>& data);
void print_data_dump_hex(const std::vector<int>& data);

int main() {
    // Write C++ code here
    std::vector v = {20,30,40,50};
    
    print_data_dump(v);
    print_data_dump_hex(v);
    print_data_dump(v);
    
    return 0;
}

void print_data_dump(const std::vector<int>& data) {
    for (int value : data) {
        // 循环体内代码很干净,无需重复设置格式
        std::cout << std::setw(2) << value << " ";
    }
    std::cout << std::endl;
}

// 场景:输出一个十六进制数据Dump
void print_data_dump_hex(const std::vector<int>& data) {
    // 在函数入口处设置一次风格
    std::ios_base::fmtflags original_flags = std::cout.flags();
    std::cout.setf(std::ios::hex | std::ios::uppercase, std::ios::basefield);
    std::cout.fill('0');
    
    print_data_dump(data);

    // 函数结束前恢复,不影响其他代码
    std::cout.flags(original_flags);
}

这个程序会输出

20 30 40 50 
14 1e 28 32 
20 30 40 50 

=== Code Execution Successful ===

可以看出,flag的效果会只限于 print_data_dump_hex 内,不会对外部的程序造成影响。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值