【C++ 格式化输出 】C++20 现代C++格式化:拥抱std--format简化你的代码

简介: 【C++ 格式化输出 】C++20 现代C++格式化:拥抱std--format简化你的代码

1. 引言

在本文中,我们将详细讨论C++格式化的传统问题与挑战,以及C++20标准中引入std::format的背景。

传统C++格式化的问题与挑战

传统C++格式化存在一些问题与挑战,主要包括以下几点:

  1. 可读性差:使用C++中的printfscanf家族函数进行格式化输出和输入时,它们的语法较为复杂,难以阅读。在较大的代码项目中,可读性差会导致维护困难。
  2. 类型安全性差printfscanf等函数无法在编译期间检查参数的类型是否正确。这可能导致运行时错误,甚至引发程序崩溃。
  3. 不够灵活:对于复杂的格式化需求,printfscanf等函数提供的功能有限。例如,它们不支持自定义类型的格式化,也不方便处理宽字符和多字节字符集。
  4. 性能开销:由于传统的格式化方法在运行时需要处理格式字符串,它们可能导致额外的性能开销。

C++20引入std::format的背景

鉴于传统C++格式化方法的局限性,C++20标准中引入了std::format库,旨在提供一种更现代、更安全、更灵活的格式化方法。引入std::format的主要动机包括:

  1. 提高可读性std::format采用了一种更加简洁、易懂的语法,使得格式化字符串更具可读性。
  2. 增强类型安全std::format在编译期间就可以检查参数类型的正确性,从而降低运行时错误的风险。
  3. 扩展功能std::format支持自定义类型的格式化,同时兼容宽字符和多字节字符集。这使得开发人员能够满足更为复杂的格式化需求。
  4. 性能优化std::format设计时充分考虑了性能问题,相比传统的格式化方法,它在许多场景下能够提供更高的性能。

总之,std::format作为C++20标准的一部分,旨在解决传统C++格式化方法的问题,并为开发者提供一种更现代、更安全、更灵活的格式化工具。

2. std::format简介

在本节中,我们将简要介绍std::format的基本概念,并对比std::formatprintfiostreams之间的差异。

std::format的基本概念

std::format是C++20标准库中新增的一个格式化工具,它基于Python中的str.format()函数,提供了一种类型安全且易于阅读的字符串格式化方法。std::format的主要特点包括:

  1. 替换字段std::format使用花括号{}作为替换字段的占位符。这些替换字段在格式化时会被相应的参数值替换。
  2. 格式规范std::format支持在替换字段内部定义格式规范,例如指定输出宽度、对齐方式和填充字符等。格式规范使用冒号:分隔,放在花括号内。
  3. 编译时类型检查std::format在编译期间检查参数类型的正确性,以提高类型安全性。
  4. 自定义类型支持std::format可以通过重载formatter特化来支持自定义类型的格式化。

std::format与printf、iostreams的对比

下面我们将对比std::formatprintfiostreams之间的主要差异:

  1. 可读性std::format使用花括号作为占位符,并允许在占位符内定义格式规范。这使得格式化字符串更具可读性,相较于printfiostreams更为简洁明了。
    示例:
std::cout << std::format("Hello, {}!\n", "World"); // std::format
printf("Hello, %s!\n", "World");                  // printf
std::cout << "Hello, " << "World" << "!\n";       // iostreams
  1. 类型安全std::format在编译期间检查参数类型的正确性,而printf在运行时检查类型。iostreams也具有类型安全性,但std::format更接近printf的语法,使得从printf迁移到std::format更容易。
  2. 扩展性std::format支持自定义类型的格式化,而printf仅支持内置类型。iostreams通过重载插入和提取操作符支持自定义类型,但std::format提供更为统一的扩展方法。
  3. 性能std::format在设计时充分考虑了性能问题,因此在许多场景下性能优于iostreams。而与printf相比,std::format的性能表现也非常出色。

综上所述,std::format在可读性、类型安全性、扩展性和性能方面都表现优异,成为现代C++编程中推荐的字符串格式化工具。

高效使用std::format的理由

以下是为什么应该高效使用std::format的几个理由:

  1. 统一的格式化语法std::format提供了一种统一的格式化语法,无论是内置类型还是自定义类型,都可以使用相同的方法进行格式化。这有助于简化代码并降低维护成本。
  2. 简化代码:由于std::format提供了更简洁的语法,使用它可以减少代码量,使代码更易于理解。相较于printfiostreamsstd::format更适合处理复杂的字符串格式化需求。
  3. 避免运行时错误std::format在编译期间检查参数类型,能够减少因类型错误导致的运行时错误。这有助于提高代码的健壮性和稳定性。
  4. 易于迁移:对于已经习惯使用printf的开发者,std::format提供了类似的语法和功能,可以轻松从printf迁移到std::format
  5. 便于调试和优化std::format的性能表现优异,且支持各种格式化选项,方便开发者进行调试和性能优化。

总之,std::format作为C++20标准库的一部分,为开发者提供了强大、易用的字符串格式化工具。使用std::format可以简化代码、提高可读性、增强类型安全性,并有助于提高代码的健壮性和性能。因此,在现代C++编程中,高效使用std::format是非常重要的。

3. 基本用法

在本节中,我们将介绍std::format的基本用法,包括格式字符串与占位符、类型规格与格式选项的使用。

格式字符串与占位符

std::format使用格式字符串来定义输出的格式。格式字符串中的占位符用花括号{}表示,可以包含以下几个部分:

  • 参数索引:位于花括号内的数字,用于指定要替换的参数的位置。例如,{0}表示第一个参数,{1}表示第二个参数,依此类推。
  • 格式规范:位于冒号:之后的部分,用于指定参数的格式选项。例如,{:d}表示将参数格式化为十进制整数。
  • 文本:花括号之间可以包含任意文本,这些文本将原样输出。例如,{0} is {1}中的is会原样输出。

以下是一些基本的例子:

#include <iostream>
#include <format>
int main() {
    int age = 30;
    double pi = 3.1415926;
    std::string name = "Alice";
    std::cout << std::format("My name is {0} and I am {1} years old.\n", name, age);
    std::cout << std::format("Pi is approximately {0}.\n", pi);
    return 0;
}

类型规格与格式选项

std::format支持各种类型规格与格式选项,以便对输出进行详细的控制。以下是一些常见的类型规格与格式选项:

  1. 整数
  • d:十进制整数。
  • x:小写十六进制整数。
  • X:大写十六进制整数。
  • o:八进制整数。
  • b:二进制整数。
    示例:
std::cout << std::format("{0:d} {0:x} {0:X} {0:o} {0:b}\n", 42);
  1. 浮点数
  • f:固定点表示法。
  • e:小写科学计数法。
  • E:大写科学计数法。
  • g:根据值选择最简表示法(fe)。
  • G:根据值选择最简表示法(fE)。
    示例:
std::cout << std::format("{0:f} {0:e} {0:E} {0:g} {0:G}\n", 3.1415926535);
  1. 字符串
  • s:字符串。
    示例:
std::cout << std::format("{:s}\n", "Hello, World!");
  1. 宽度、对齐和填充
  • <:左对齐。
  • >:右对齐。
  • ^:居中对齐。
  • 数字:指定输出宽度。
  • 字符:指定填充字符。
    示例:
std::cout << std::format("{:<10} | {:>10} | {:^10}\n", "left", "right", "center");
std::cout << std::format("{:*<10} | {:#>10} | {:_^10}\n", "left", "right", "center");
  1. 精度
    对于浮点数,精度用于指定小数点后的位数;对于字符串,精度用于指定最大输出长度。
    示例:
std::cout << std::format("{:.2f} | {:.3e} | {:.4s}\n", 3.1415926, 12345.6789, "abcdefgh");
  1. 整数和浮点数的进位
    整数和浮点数的进位可以使用#选项,它会在八进制和十六进制数字前添加00x0X)前缀,或在浮点数上强制输出小数点。
    示例:
std::cout << std::format("{:#x} | {:#o} | {:#f}\n", 42, 42, 3.14);
  1. 正负号
    使用+选项可以强制输出正数的正号。
    示例:
std::cout << std::format("{:+d} | {:+f}\n", 42, 3.14);
  1. 自定义类型
    : 要格式化自定义类型,需要为类型特化std::formatter模板,并提供parseformat成员函数。这使得std::format可以以一种统一的方式处理内置类型和自定义类型。
    示例:
struct Point {
    int x, y;
};
template<>
struct std::formatter<Point> {
    auto parse(format_parse_context& ctx) {
        return ctx.begin();
    }
    auto format(const Point& p, format_context& ctx) {
        return std::format_to(ctx.out(), "({:d}, {:d})", p.x, p.y);
    }
};
std::cout << std::format("{0}\n", Point{3, 4});

4. 格式化数字

在使用std::format时,您可能会需要更多地控制数字的格式。在本节中,我们将详细讨论数字格式化的选项,包括宽度、精度、填充、正负号显示、进制转换以及浮点数格式化选项。

控制数字的宽度、精度与填充

要控制数字的宽度,请在格式说明符中指定一个整数。此外,您还可以使用0指定填充字符,例如{:05}表示将数字格式化为至少5个字符宽,不足部分用零填充。以下是一些示例:

std::cout << std::format("{:5}", 42); // "   42"
std::cout << std::format("{:05}", 42); // "00042"

对于浮点数,您可以使用.后接一个整数来指定精度。例如:

std::cout << std::format("{:.2f}", 3.14159); // "3.14"

显示或隐藏正负号

要显示数字的正负号,可以使用+标志。例如:

std::cout << std::format("{:+}", 42); // "+42"
std::cout << std::format("{:+}", -42); // "-42"

进制转换(十进制、十六进制、八进制等)

要将数字格式化为其他进制,可以使用以下格式说明符:

  • d:十进制(默认)
  • x:十六进制(小写字母)
  • X:十六进制(大写字母)
  • o:八进制
  • b:二进制(小写字母)
  • B:二进制(大写字母)

以下是一些示例:

std::cout << std::format("{:x}", 42); // "2a"
std::cout << std::format("{:X}", 42); // "2A"
std::cout << std::format("{:o}", 42); // "52"
std::cout << std::format("{:b}", 42); // "101010"

浮点数格式化选项

对于浮点数,您可以使用以下格式说明符:

  • f:定点表示(默认)
  • F:定点表示(无穷大和非数字为大写表示)
  • e:科学计数法(小写字母)
  • E:科学计数法(大写字母)
  • g:通用格式,根据值的大小和指定精度自动选择定点表示或科学计数法(小写字母)
  • G:通用格式,根据值的大小和指定精度自动选择定点表示或科学计数法(大写字母)

以下是一些示例:

std::cout << std::format("{:.2F}", 42.123); // "42.12"
std::cout << std::format("{:.2e}", 42.123); // "4.21e+01"
std::cout << std::format("{:.2E}", 42.123); // "4.21E+01"
std::cout << std::format("{:.2g}", 42.123); // "42.12"
std::cout << std::format("{:.2G}", 42.123); // "42.12"
std::cout << std::format("{:.2g}", 0.000421); // "0.000421"
std::cout << std::format("{:.2G}", 0.000421); // "0.000421"

通过上述示例,您可以看到不同浮点数格式化选项的使用方法。这使得std::format成为一个非常灵活和强大的工具,能够处理各种数字格式化需求。请尝试根据您的应用需求进行调整和组合这些选项。

5. 格式化文本

在使用std::format时,除了处理数字之外,您还需要考虑如何格式化文本。本节将讨论如何使用std::format来处理字符串的宽度、填充、特殊字符、转义以及多语言和Unicode字符。

控制字符串的宽度与填充

要设置字符串的最小宽度,请在格式说明符中指定一个整数。您还可以通过在整数前加上填充字符来设置填充字符。以下是一些示例:

std::cout << std::format("{:10}", "hello"); // "hello     "
std::cout << std::format("{:_<10}", "hello"); // "hello_____"

处理特殊字符与转义

要在格式化字符串中包含大括号{},请使用两个连续的大括号{{}}进行转义。以下是一个示例:

std::cout << std::format("The set contains {{1, 2, 3}}"); // "The set contains {1, 2, 3}"

要在格式化字符串中包含反斜杠和其他特殊字符,请使用反斜杠进行转义,如\n表示换行符,\t表示制表符等。例如:

std::cout << std::format("Line 1\\nLine 2"); // "Line 1\nLine 2"

使用std::format处理多语言与Unicode

std::format支持Unicode字符和多语言文本处理。为了确保正确处理Unicode字符,请使用u8前缀表示UTF-8编码的字符串字面值。以下是一个示例:

std::cout << std::format(u8"你好,世界!"); // "你好,世界!"

在处理Unicode字符串时,确保使用正确的编码,否则可能会导致乱码或无法解释的字符。std::format兼容C++17及更高版本的std::u8string类型,允许您更轻松地处理多语言文本。

总之,std::format提供了处理字符串宽度、填充、特殊字符、转义以及多语言和Unicode字符的能力。这使得std::format成为一个非常适用于现代C++应用程序的强大工具。

6. 格式化日期与时间

std::format可以与C++的chrono库一起使用,方便地格式化日期和时间。本节将讨论如何使用std::format处理时间点、持续时间、时间格式化选项以及本地化日期和时间的显示。

使用chrono库处理时间点与持续时间

chrono库提供了表示时间点和持续时间的类,如system_clock::time_pointsteady_clock::time_pointduration等。要使用std::format格式化这些类型,首先需要包含头文件。

以下是一个示例:

#include <chrono>
#include <format>
#include <iostream>
int main() {
    auto now = std::chrono::system_clock::now();
    auto seconds_since_epoch = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());
    std::cout << std::format("Seconds since epoch: {}\n", seconds_since_epoch.count());
}

时间格式化选项

要格式化日期和时间,可以使用扩展的格式说明符,如下所示:

  • %Y:四位年份
  • %m:月份(01-12)
  • %d:月份中的第几天(01-31)
  • %H:小时(00-23)
  • %M:分钟(00-59)
  • %S:秒(00-60,因闰秒可能为60)

为了使用这些格式化选项,需要先将chrono中的time_point转换为std::tm结构,并包含头文件。以下是一个示例:

#include <chrono>
#include <format>
#include <iomanip>
#include <iostream>
int main() {
    auto now = std::chrono::system_clock::now();
    auto now_t = std::chrono::system_clock::to_time_t(now);
    auto now_tm = *std::localtime(&now_t);
    std::cout << std::format("{:%Y-%m-%d %H:%M:%S}\n", now_tm);
}

本地化日期与时间的显示

要显示本地化的日期和时间,可以使用std::locale。使用imbue()函数将流与特定的语言环境关联起来。以下是一个示例:

#include <chrono>
#include <format>
#include <iomanip>
#include <iostream>
#include <locale>
int main() {
    auto now = std::chrono::system_clock::now();
    auto now_t = std::chrono::system_clock::to_time_t(now);
    auto now_tm = *std::localtime(&now_t);
    std::locale::global(std::locale(""));
    std::cout.imbue(std::locale());
    std::cout << std::format("{:%c}\n", now_tm);
}

请注意,std::locale::global()imbue()函数的参数取决于您的平台和语言设置。本示例设置为系统默认语言环境。您还可以为特定的流或字符串指定语言环境。

通过以上方法,您可以使用std::format来灵活地处理和格式化日期与时间。与C++的chrono库结合使用,可以更方便地处理时间点和持续时间,同时允许您定制时间格式化选项以适应不同的应用场景。同时,通过std::locale类,您还可以实现日期和时间的本地化显示,以适应不同地区的用户。

下面是一些日期和时间格式化选项的示例:

#include <chrono>
#include <format>
#include <iomanip>
#include <iostream>
#include <locale>
int main() {
    auto now = std::chrono::system_clock::now();
    auto now_t = std::chrono::system_clock::to_time_t(now);
    auto now_tm = *std::localtime(&now_t);
    std::cout << std::format("{:%A, %B %d, %Y}\n", now_tm); // 显示星期、月份、日期和年份,例如:"Sunday, April 09, 2023"
    std::cout << std::format("{:%D}\n", now_tm); // 以MM/DD/YY格式显示日期,例如:"04/09/23"
    std::cout << std::format("{:%T}\n", now_tm); // 以HH:MM:SS格式显示时间,例如:"17:30:59"
    std::cout << std::format("{:%r}\n", now_tm); // 以12小时制显示时间,例如:"05:30:59 PM"
}

7. 自定义类型的格式化

std::format允许您为自定义类型实现格式化支持,这为您的自定义类型提供了更好的输出显示。要实现自定义类型的格式化支持,您需要特化std::formatter。在本节中,我们将讨论如何为自定义类型实现格式化输出。

实现自定义类型的格式化支持

要为自定义类型实现格式化支持,您需要为其特化std::formatter,并重载parse()format()成员函数。

以下是实现自定义类型格式化输出的步骤:

  1. 包含头文件。
  2. 为您的自定义类型特化std::formatter
  3. 在特化的std::formatter中,重载parse()format()成员函数。

使用fmt::formatter特化

以下是一个简单的自定义类型(Person)和std::formatter特化的示例:

#include <format>
#include <iostream>
#include <string>
struct Person {
    std::string name;
    int age;
};
template <>
struct std::formatter<Person> {
    constexpr auto parse(format_parse_context& ctx) {
        auto it = ctx.begin();
        auto end = ctx.end();
        if (it != end && *it != '}')
            throw format_error("Invalid format");
        return it;
    }
    auto format(const Person& p, format_context& ctx) {
        return format_to(ctx.out(), "{} ({})", p.name, p.age);
    }
};

示例:为自定义类型实现格式化输出

现在我们已经为Person类型实现了std::formatter特化,可以使用std::format函数轻松格式化Person对象了:

int main() {
    Person alice{"Alice", 30};
    std::cout << std::format("{}", alice) << std::endl; // 输出:"Alice (30)"
}

通过实现std::formatter特化并重载parse()format()成员函数,您可以为自定义类型提供灵活且易于使用的格式化支持。这可以大大提高您的C++代码的可读性和维护性。

8. std::format的高级技巧与应用

在本节中,我们将讨论std::format的一些高级技巧和应用,包括动态生成格式字符串、与其他标准库组件(如容器、文件操作等)的结合使用以及提高格式化性能的建议。

格式字符串的动态生成

在某些情况下,您可能需要根据运行时参数动态生成格式字符串。可以使用std::string或其他字符串处理方法来实现这一点。例如,您可能需要根据用户输入设置小数点后的位数:

#include <format>
#include <iostream>
int main() {
    double pi = 3.141592653589793;
    int precision = 2;
    std::string format_str = "{:." + std::to_string(precision) + "f}";
    std::cout << std::format(format_str, pi) << std::endl; // 输出:"3.14"
}

使用std::format与其他标准库组件(如容器、文件操作等)

std::format可以与其他标准库组件(如容器、文件操作等)一起使用,以提供更高级的格式化功能。以下是一些示例:

  • 与容器一起使用:
#include <format>
#include <iostream>
#include <vector>
int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    std::string result = std::format("Numbers: [");
    for (const auto& num : numbers) {
        result += std::format("{}, ", num);
    }
    result = result.substr(0, result.size() - 2) + "]";
    std::cout << result << std::endl; // 输出:"Numbers: [1, 2, 3, 4, 5]"
}
  • 文件操作一起使用:
#include <format>
#include <fstream>
#include <iostream>
int main() {
    std::ofstream output_file("output.txt");
    output_file << std::format("{:<10} {:>10}\n", "Name", "Score");
    output_file << std::format("{:<10} {:>10}\n", "Alice", 95);
    output_file << std::format("{:<10} {:>10}\n", "Bob", 80);
    output_file.close();
    std::cout << "Output saved to output.txt" << std::endl;
}

提高格式化性能的建议

虽然std::format在很多方面都比传统的格式化方法更高效,但在某些情况下,性能仍然是一个值得关注的问题。以下是一些建议,可以帮助您提高格式化性能:

  1. 避免频繁构建和销毁格式化字符串:在循环或高频调用的函数中避免重复构建格式化字符串。考虑将格式化字符串预先计算并存储为常量或静态变量。
  2. 减少不必要的字符串连接:在可能的情况下,尽量避免使用+运算符连接字符串。可以使用std::format直接构建最终字符串,而不是分段拼接。例如,可以将多个std::format`调用替换为一个带有多个占位符的调用。
  3. 使用预分配的内存:为频繁使用的字符串分配足够的预先分配的内存,以减少内存分配和重新分配的开销。例如,您可以使用std::string::reserve()函数为字符串预留足够的空间。
  4. 避免不必要的类型转换:在可能的情况下,尽量避免在格式化之前将数据类型转换为其他类型。例如,不要在格式化之前将int转换为std::string,而是直接使用int类型的格式规范。
  5. 选择合适的容器和算法:根据具体应用场景选择合适的容器和算法,以实现最佳性能。例如,对于需要快速插入和删除元素的场景,使用std::liststd::deque而不是std::vector

通过遵循以上建议,您可以确保在使用std::format进行格式化操作时实现最佳性能。这将有助于提高您的C++应用程序的整体性能和响应速度。

9. 结论与展望

std::format在现代C++中的地位与作用

std::format是C++20中引入的一个重要特性,它在现代C++中扮演着重要的角色。与传统的C++格式化方法相比,如printfiostreamsstd::format提供了更为强大、灵活和安全的格式化功能。它支持类型安全,易于扩展,支持自定义类型和多语言环境。std::format有助于提高代码的可读性和维护性,使得C++在格式化方面与其他现代编程语言保持同步。

与其他语言的格式化库的比较

std::format的设计受到了其他编程语言中格式化库的启发,如Python的str.format()f-string,以及Rust的std::fmt。与这些库相比,std::format具有类似的功能和语法,同时充分利用了C++的类型系统和编译时特性,以实现最佳性能。

C++标准化进程中格式化相关的未来发展

C++标准化进程将继续发展和完善格式化功能。例如,C++23中可能会引入std::format的扩展,以提供更丰富的格式选项和本地化支持。此外,C++社区也将继续关注其他语言的发展,以确保C++在格式化方面与时俱进。

总之,std::format为C++开发者提供了一种强大且易于使用的格式化工具。它不仅带来了更好的类型安全和扩展性,还为未来的C++标准提供了一个坚实的基础。作为现代C++的一部分,std::format将继续发展和完善,为C++程序员提供更高效和灵活的格式化解决方案。

10.一百个std::format使用示例

#include <iostream>
#include <format>
int main() {
    int num = 42;
    double pi = 3.1415926535;
    std::string str = "Hello, World!";
    std::string long_str = "This is a very long string to showcase precision";
    std::cout << std::format("1. {:d}\n", num);                                   // 1. 整数十进制
    std::cout << std::format("2. {:x}\n", num);                                   // 2. 整数小写十六进制
    std::cout << std::format("3. {:X}\n", num);                                   // 3. 整数大写十六进制
    std::cout << std::format("4. {:o}\n", num);                                   // 4. 整数八进制
    std::cout << std::format("5. {:b}\n", num);                                   // 5. 整数二进制
    std::cout << std::format("6. {:f}\n", pi);                                    // 6. 浮点数固定点表示
    std::cout << std::format("7. {:e}\n", pi);                                    // 7. 浮点数小写科学计数法
    std::cout << std::format("8. {:E}\n", pi);                                    // 8. 浮点数大写科学计数法
    std::cout << std::format("9. {:g}\n", pi);                                    // 9. 浮点数简化小写表示法
    std::cout << std::format("10. {:G}\n", pi);                                   // 10. 浮点数简化大写表示法
    std::cout << std::format("11. {:s}\n", str);                                  // 11. 字符串
    std::cout << std::format("12. {:<10}\n", str);                                // 12. 左对齐
    std::cout << std::format("13. {:>10}\n", str);                                // 13. 右对齐
    std::cout << std::format("14. {:^10}\n", str);                                // 14. 居中对齐
    std::cout << std::format("15. {:*>10}\n", str);                               // 15. 指定填充字符
    std::cout << std::format("16. {:.5s}\n", long_str);                           // 16. 字符串精度
    std::cout << std::format("17. {:.2f}\n", pi);                                 // 17. 浮点数精度
    std::cout << std::format("18. {:#x}\n", num);                                 // 18. 整数进制前缀
    std::cout << std::format("19. {:#o}\n", num);                                 // 19. 整数八进制前缀
    std::cout << std::format("20. {:#f}\n", pi);                                  // 20. 浮点数强制输出小数点
    std::cout << std::format("21. {:d}\n", -num);                                 // 21. 负数整数
    std::cout << std::format("22. {:f}\n", -pi);                                  // 22. 负数浮点数
    std::cout << std::format("23. {:g}\n", 1e-10);                                // 23. 浮点数简化表示法对于极小值的处理
    std::cout << std::format("24. {:G}\n", 1e+10);                                // 24. 浮点数简化大写表示法对于极大值的处理
    std::cout << std::format("25. {:%}\n", 0.5);                                  // 25. 百分数表示
    std::cout << std::format("26. {:%}\n", 0.99);                                 // 26. 百分数表示
    std::cout << std::format("27. {:%}\n", -0.5);                                 // 27. 负数百分数表示
    std::cout << std::format("28. {:L}\n", true);                                 // 28. 布尔值为true时显示字母L
    std::cout << std::format("29. {:P}\n", false);                                // 29. 布尔值为false时显示字母P
    std::cout << std::format("30. {0:*<10} {1:#>10} {2:_^10}\n", "left", "right", "center"); // 30. 多个参数填充、对齐
    std::cout << std::format("31. {:+d}\n", num);                                 // 31. 强制输出正数的正号
    std::cout << std::format("32. {:+f}\n", pi);                                  // 32. 强制输出正数的正号
    std::cout << std::format("33. {:-d}\n", num);                                 // 33. 显示正数时省略正号
    std::cout << std::format("34. {: d}\n", num);                                 // 34. 为正数留出空格
    std::cout << std::format("35. {:10.2f}\n", pi);                               // 35. 指定宽度和精度
    std::cout << std::format("36. {0:d} - {1:s}\n", num, str);                    // 36. 指定参数位置
    std::cout << std::format("37. {:10.2e}\n", 12345.6789);                       // 37. 科学计数法指定宽度和精度
    std::cout << std::format("38. {:05d}\n", num);                                // 38. 整数补零
    std::cout << std::format("39. {:010.2f}\n", pi);                              // 39. 浮点数补零
    std::cout << std::format("40. {:+010.2f}\n", pi);                             // 40. 浮点数补零并强制输出正号
    std::cout << std::format("41. {0:*<10} {1:#>10}\n", str, num);                // 41. 分别指定左填充和右填充
    std::cout << std::format("42. {0:.>{1}}\n", str, 15);                         // 42. 动态指定宽度
    std::cout << std::format("43. {0:^{1}}\n", str, 20);                          // 43. 动态指定居中对齐的宽度
    std::cout << std::format("44. {0:*>{1}.>{2}}\n", long_str, 20, 10);           // 44. 动态指定宽度、精度和填充字符
    std::cout << std::format("45. {:n}\n", 1000000);                              // 45. 使用本地化的千位分隔符
    std::cout << std::format("46. {:04X}\n", num);                                // 46. 大写十六进制补零
    std::cout << std::format("47. {:c}\n", 65);                                   // 47. 将整数解释为ASCII字符
    std::cout << std::format("48. {:010,d}\n", 1000000);                          // 48. 整数千位分隔符和补零
    std::cout << std::format("49. {:20,}\n", 1234567890);                         // 49. 整数千位分隔符和宽度
    std::cout << std::format("50. {0:%}{1:->10}\n", 0.5, "right");                // 50. 百分数表示与右对齐结合
    std::cout << std::format("51. {0:x}{1:10.2f}\n", num, pi);                    // 51. 同一行输出十六进制整数和浮点数
    std::cout << std::format("52. {0:<10}{1:>10}\n", str, num);                   // 52. 左对齐字符串和右对齐数字
    std::cout << std::format("53. {0:*^20s}\n", str);                             // 53. 居中字符串并指定填充字符
    std::cout << std::format("54. {:{},.{}f}\n", 1234567.89, 15, 2);              // 54. 动态指定宽度、精度和千位分隔符
    std::cout << std::format("55. {:A}\n", 0x1.921fb54442d18p+1);                 // 55. 十六进制浮点数大写表示
    std::cout << std::format("56. {:a}\n", 0x1.921fb54442d18p+1);                 // 56. 十六进制浮点数小写表示
    std::cout << std::format("57. {:%.2%}\n", 0.123456);                          // 57. 百分数表示并指定精度
    std::cout << std::format("58. {:0>10}\n", num);                               // 58. 数字补零并右对齐
    std::cout << std::format("59. {:0>10}\n", str);                               // 59. 字符串补零并右对齐
    std::cout << std::format("60. {0:#010x} {1:.{}f}\n", num, pi, 3);             // 60. 整数进制前缀、浮点数动态精度
    std::cout << std::format("61. {0:0{1}X}\n", num, 6);                          // 61. 动态指定整数十六进制大写表示的宽度
    std::cout << std::format("62. {0:0{1}x}\n", num, 8);                          // 62. 动态指定整数十六进制小写表示的宽度
    std::cout << std::format("63. {0:0{1}d}\n", num, 10);                         // 63. 动态指定整数十进制表示的宽度
    std::cout << std::format("64. {0:0{1}o}\n", num, 8);                          // 64. 动态指定整数八进制表示的宽度
    std::cout << std::format("66. {:0{width}.{}f}\n", pi, 3, width = 10);         // 66. 动态指定浮点数的宽度和精度
    std::cout << std::format("67. {:{fill}>{width}}\n", str, fill='-', width=15); // 67. 动态指定字符串右对齐的宽度和填充字符
    std::cout << std::format("68. {0:{fill}<{width}}\n", str, fill='-', width=15);// 68. 动态指定字符串左对齐的宽度和填充字符
    std::cout << std::format("69. {0:0{1},}\n", 1234567890, 15);                  // 69. 整数动态指定宽度、千位分隔符
    std::cout << std::format("70. {0:s} {1:x}\n", "String", num);                 // 70. 在同一行输出字符串和小写十六进制整数
    std::cout << std::format("71. {:08b}\n", num);                                // 71. 补零的二进制整数表示
    std::cout << std::format("72. {0:+>10} {1:-^10}\n", str, num);                // 72. 同时使用填充字符和对齐方式
    std::cout << std::format("73. {0:s} {1:+>10}\n", long_str, num);              // 73. 不同类型的参数使用不同对齐方式
    std::cout << std::format("74. {0:.{1}s}\n", long_str, 10);                    // 74. 动态指定字符串截断长度
    std::cout << std::format("75. {0:<{1}s}\n", long_str, 10);                    // 75. 动态指定字符串左对齐的宽度
    std::cout << std::format("76. {0:.>{1},.{}f}\n", 1234567.89, 15, 2);          // 76. 动态指定浮点数宽度、精度和千位分隔符
    std::cout << std::format("77. {0:.^20}\n", long_str);                         // 77. 居中对齐较长的字符串
    std::cout << std::format("78. {0:.>{1}}\n", long_str, 30);                    // 78. 动态指定字符串右对齐的宽度
    std::cout << std::format("79. {0:.<{1}}\n", long_str, 30);                    // 79. 动态指定字符串左对齐的宽度
    std::cout << std::format("80. {0:.{1}e}\n", pi, 5);                           // 80. 动态指定科学计数法的精度
    std::cout << std::format("81. {0:.{1}E}\n", pi, 5);                           // 81. 动态指定科学计数法的精度(大写表示)
    std::cout << std::format("82. {0:{1}{2}{3}}\n", pi, '>', 10, 'f');            // 82. 分别指定对齐、宽度和格式字符
    std::cout << std::format("83. {0:s} {1:+>10.{}f}\n", long_str, pi, 3);        // 83. 字符串与带符号的浮点数动态精度
    std::cout << std::format("84. {:5}\n", 123);                                  // 84. 整数使用最小宽度
    std::cout << std::format("85. {:05}\n", 123);                                 // 85. 整数使用最小宽度和补零
    std::cout << std::format("86. {:5}\n", "123");                                // 86. 字符串使用最小宽度
    std::cout << std::format("87. {:>5}\n", "123");                               // 87. 字符串使用最小宽度和右对齐
    std::cout << std::format("88. {:^5}\n", "123");                               // 88. 字符串使用最小宽度和居中对齐
    std::cout << std::format("89. {:<5}\n", "123");                               // 89. 字符串使用最小宽度和左对齐
    std::cout << std::format("90. {:{}}\n", 123, 5);                              // 90. 动态指定整数的最小宽度
    std::cout << std::format("91. {:{}}\n", "123", 5);                            // 91. 动态指定字符串的最小宽度
    std::cout << std::format("92. {0:s} {1:+0{2}.{}f}\n", long_str, pi, 10, 3);   // 92. 字符串与带符号的浮点数动态宽度、精度
    std::cout << std::format("93. {0:s} {1:+0{2},.{}f}\n", long_str, 1234567.89, 15, 2); // 93. 字符串与符号的浮点数带小数点
    std::cout << std::format("94. {0:#x} {1:#X}\n", num, num);                    // 94. 十六进制表示,带前缀,分别为小写和大写
    std::cout << std::format("95. {0:#o}\n", num);                                // 95. 带前缀的八进制表示
    std::cout << std::format("96. {0:#b} {1:#B}\n", num, num);                    // 96. 带前缀的二进制表示,分别为小写和大写
    std::cout << std::format("97. {0:#0{1}X}\n", num, 6);                         // 97. 带前缀的十六进制表示,指定宽度和补零
    std::cout << std::format("98. {0:#0{1}o}\n", num, 6);                         // 98. 带前缀的八进制表示,指定宽度和补零
    std::cout << std::format("99. {0:#0{1}b}\n", num, 10);                        // 99. 带前缀的二进制表示,指定宽度和补零
    std::cout << std::format("100. {0:s} {1:#0{2}.{}f}\n", long_str, pi, 10, 3);  // 100. 字符串与带前缀的浮点数动态宽度、精度
} 


目录
相关文章
|
3月前
|
C++
C++ 语言异常处理实战:在编程潮流中坚守稳定,开启代码可靠之旅
【8月更文挑战第22天】C++的异常处理机制是确保程序稳定的关键特性。它允许程序在遇到错误时优雅地响应而非直接崩溃。通过`throw`抛出异常,并用`catch`捕获处理,可使程序控制流跳转至错误处理代码。例如,在进行除法运算或文件读取时,若发生除数为零或文件无法打开等错误,则可通过抛出异常并在调用处捕获来妥善处理这些情况。恰当使用异常处理能显著提升程序的健壮性和维护性。
72 2
|
3月前
|
算法框架/工具 C++ Python
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
210 0
|
5月前
|
编译器 C++ 开发者
C++一分钟之-C++20新特性:模块化编程
【6月更文挑战第27天】C++20引入模块化编程,缓解`#include`带来的编译时间长和头文件管理难题。模块由接口(`.cppm`)和实现(`.cpp`)组成,使用`import`导入。常见问题包括兼容性、设计不当、暴露私有细节和编译器支持。避免这些问题需分阶段迁移、合理设计、明确接口和关注编译器更新。示例展示了模块定义和使用,提升代码组织和维护性。随着编译器支持加强,模块化将成为C++标准的关键特性。
286 3
|
1月前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
172 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
2月前
|
C++
继续更新完善:C++ 结构体代码转MASM32代码
继续更新完善:C++ 结构体代码转MASM32代码
|
2月前
|
C++ Windows
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
|
2月前
|
C++
2合1,整合C++类(Class)代码转换为MASM32代码的平台
2合1,整合C++类(Class)代码转换为MASM32代码的平台
|
2月前
|
前端开发 C++ Windows
C++生成QML代码与QML里面集成QWidget
这篇文章介绍了如何在C++中生成QML代码,以及如何在QML中集成QWidget,包括使用Qt Widgets嵌入到QML界面中的技术示例。
|
3月前
|
程序员 C++ 开发者
C++命名空间揭秘:一招解决全局冲突,让你的代码模块化战斗值飙升!
【8月更文挑战第22天】在C++中,命名空间是解决命名冲突的关键机制,它帮助开发者组织代码并提升可维护性。本文通过一个图形库开发案例,展示了如何利用命名空间避免圆形和矩形类间的命名冲突。通过定义和实现这些类,并在主函数中使用命名空间创建对象及调用方法,我们不仅解决了冲突问题,还提高了代码的模块化程度和组织结构。这为实际项目开发提供了宝贵的参考经验。
60 2
|
3月前
|
C++
拥抱C++面向对象编程,解锁软件开发新境界!从混乱到有序,你的代码也能成为高效能战士!
【8月更文挑战第22天】C++凭借其强大的面向对象编程(OOP)能力,在构建复杂软件系统时不可或缺。OOP通过封装数据和操作这些数据的方法于对象中,提升了代码的模块化、重用性和可扩展性。非OOP方式(过程化编程)下,数据与处理逻辑分离,导致维护困难。而OOP将学生信息及其操作整合到`Student`类中,增强代码的可读性和可维护性。通过示例对比,可以看出OOP使C++代码结构更清晰,特别是在大型项目中,能有效提高开发效率和软件质量。
32 1