Formatação de Saída com fmt::format em C++

A biblioteca fmt é integrada diretamente ao spdlog, disponibilizando toda a sintaxe de formatação do fmt para uso imediato.

A sintaxe completa de um especificador de formato é:

{[índice]:[alinhamento][sinal][#][0][largura][.precisão][tipo]}

  1. Formatação básica de números

Exemplos com inteiros, hexadecimais e notação científica.

#include <fmt/format.h>
#include <iostream>
#include <cmath>

int main() {
    int valor = 255;
    unsigned uvalor = 4294967295u;
    double cient = 12345.6789;

    // Decimal
    auto res = fmt::format("Decimal: {}", valor);
    std::cout << res << "\n";                     // Decimal: 255

    res = fmt::format("Decimal (sem sinal): {}", uvalor);
    std::cout << res << "\n";                    // Decimal (sem sinal): 4294967295

    // Hexadecimal
    res = fmt::format("hex minúsculo: {:x}", valor);
    std::cout << res << "\n";                    // hex minúsculo: ff

    res = fmt::format("hex maiúsculo: {:X}", valor);
    std::cout << res << "\n";                    // hex maiúsculo: FF

    res = fmt::format("hex com prefixo: {:#x}", valor);
    std::cout << res << "\n";                    // hex com prefixo: 0xff

    res = fmt::format("hex com prefixo maiúsculo: {:#X}", valor);
    std::cout << res << "\n";                    // hex com prefixo maiúsculo: 0XFF

    // Notação científica
    res = fmt::format("científica (e): {:e}", cient);
    std::cout << res << "\n";                    // científica (e): 1.234568e+04

    res = fmt::format("científica (E): {:E}", cient);
    std::cout << res << "\n";                    // científica (E): 1.234568E+04

    return 0;
}

  1. Largura, alinhamento e preenchimento

Controel de espaçamento e posicionamento.

#include <fmt/format.h>
#include <iostream>

int main() {
    int num = 42;
    double flt = 123.456;

    std::cout << "=== Largura ===\n";
    auto r = fmt::format("largura 10: |{:10}|", num);
    std::cout << r << "\n";                       // |        42|

    r = fmt::format("largura 10 (float): |{:10}|", flt);
    std::cout << r << "\n";                       // |   123.456|

    r = fmt::format("hex largura 8: |{:8x}|", 0xFF);
    std::cout << r << "\n";                       // |      ff|

    r = fmt::format("hex largura 8 com zeros: |{:08x}|", 0xFF);
    std::cout << r << "\n";                       // |000000ff|

    std::cout << "\n=== Alinhamento ===\n";
    r = fmt::format("esquerda: |{:<10}|", num);
    std::cout << r << "\n";                        // |42        |

    r = fmt::format("direita (padrão): |{:>10}|", num);
    std::cout << r << "\n";                        // |        42|

    r = fmt::format("centro: |{:^10}|", num);
    std::cout << r << "\n";                        // |    42    |

    r = fmt::format("preencher com '*': |{:*^10}|", num);
    std::cout << r << "\n";                        // |****42****|

    r = fmt::format("preencher com '-', direita: |{:->10}|", num);
    std::cout << r << "\n";                        // |--------42|

    return 0;
}

  1. Precisão de ponto flutuante

Controla o número de casas decimais.

#include <fmt/format.h>
#include <iostream>
#include <cmath>

int main() {
    double pi = M_PI;
    double grande = 1234567.8901234567;
    double pequeno = 0.00000123456789;

    std::cout << "=== Precisão ===\n";
    auto r = fmt::format("padrão: {}", pi);
    std::cout << r << "\n";                           // 3.141592653589793

    r = fmt::format("2 casas: {:.2f}", pi);
    std::cout << r << "\n";                             // 3.14

    r = fmt::format("5 casas: {:.5f}", pi);
    std::cout << r << "\n";                             // 3.14159

    r = fmt::format("not. científica com 2 casas: {:.2e}", grande);
    std::cout << r << "\n";                             // 1.23e+06

    r = fmt::format("not. científica (E) 2 casas: {:.2E}", grande);
    std::cout << r << "\n";                             // 1.23E+06

    r = fmt::format("automática (g) 3 casas: {:.3g}", pi);
    std::cout << r << "\n";                             // 3.14

    return 0;
}

  1. Combinações comuns

Exemplos que unem largura, alinhamento, precisão e tipo.

#include <fmt/format.h>
#include <iostream>

int main() {
    std::cout << "=== Combinações ===\n";
    auto r = fmt::format("inteiro: |{:0>10d}|", 42);
    std::cout << r << "\n";                         // |0000000042|

    r = fmt::format("hex: |{:*<10x}|", 255);
    std::cout << r << "\n";                         // |ff********|

    r = fmt::format("hex maiúsculo centralizado: |{:#^12X}|", 0xBEEF);
    std::cout << r << "\n";                         // |  0XBEEF   |

    r = fmt::format("float à direita: |{:>10.2f}|", 123.456);
    std::cout << r << "\n";                         // |    123.46|

    r = fmt::format("float à esquerda: |{:-<10.3f}|", 123.456);
    std::cout << r << "\n";                         // |123.456---|

    r = fmt::format("hex com prefixo e zeros: |{:#010x}|", 255);
    std::cout << r << "\n";                         // |0x000000ff|

    return 0;
}

  1. Largura e precisão dinâmicas

Os valores de largura e precisão podem ser passados como argumentos adicionais.

#include <fmt/format.h>
#include <iostream>

int main() {
    int larg = 15;
    int prec = 6;
    double val = 123.456789;

    auto r = fmt::format("largura dinâmica: |{:{}}|", val, larg);
    std::cout << r << "\n";                           // |     123.457|

    r = fmt::format("largura e precisão: |{:{}.{}f}|", val, larg, prec);
    std::cout << r << "\n";                           // |    123.456789|

    int hex_larg = 8;
    r = fmt::format("hex dinâmico: |{:0{}x}|", 255, hex_larg);
    std::cout << r << "\n";                           // |000000ff|

    r = fmt::format("hex com prefixo dinâmico: |{:#0{}X}|", 255, hex_larg + 2);
    std::cout << r << "\n";                           // |0X0000FF|

    return 0;
}

  1. Funções utilitárias para formatação

Encapsulamento de formatadores com parâmetros reutilizáveis.

#include <fmt/format.h>
#include <iostream>

std::string formataHex(uint64_t valor, int largura = 0, char alinhamento = '>', bool maiusculo = false, bool prefixo = false) {
    std::string fmt_str = "{:";
    if (alinhamento != '>') fmt_str += alinhamento;
    if (largura > 0) {
        if (prefixo) fmt_str += "0";
        fmt_str += std::to_string(largura);
    }
    if (prefixo) fmt_str += "#";
    fmt_str += maiusculo ? "X}" : "x}";
    return fmt::format(fmt::runtime(fmt_str), valor);
}

std::string formataCientifica(double valor, int precisao = 6, int largura = 0, char alinhamento = '>', bool maiusculo = false) {
    std::string fmt_str = "{:";
    if (alinhamento != '>') fmt_str += alinhamento;
    if (largura > 0) fmt_str += std::to_string(largura);
    fmt_str += "." + std::to_string(precisao);
    fmt_str += maiusculo ? "E}" : "e}";
    return fmt::format(fmt::runtime(fmt_str), valor);
}

int main() {
    std::cout << "Utilitários:\n";
    auto r = formataHex(0xABCD, 8);
    std::cout << "hex largura 8: " << r << "\n";              //     abcd

    r = formataHex(0xBEEF, 12, '>', false, true);
    std::cout << "hex com prefixo: " << r << "\n";            //        0xbeef

    r = formataCientifica(123456.789, 3, 15, '>', true);
    std::cout << "científica: " << r << "\n";                //       1.235E+05

    return 0;
}

  1. Exemplo prático: tabela de sensores

Formatação de múltiplas colunas com diferentes tipos.

#include <fmt/format.h>
#include <iostream>
#include <vector>

struct Sensor {
    int id;
    unsigned raw;
    double tensao;
    double temp;
};

int main() {
    std::vector<Sensor> dados = {
        {1, 0x1A3F, 3.14159265, 25.123456},
        {2, 0xBEEF, 5.0, -10.56789},
        {3, 0x00FF, 12.3456789, 100.0},
        {4, 0xABCD, 0.00123456, 0.0}
    };

    std::cout << "Tabela de Sensores\n";
    std::cout << fmt::format("{:─^80}\n", "");
    std::cout << fmt::format("{:<4} | {:<10} | {:<12} | {:<15} | {:<20}\n",
                              "ID", "Raw (Hex)", "Tensão (V)", "Temperatura (C)", "Notação Cient.");
    std::cout << fmt::format("{:─^80}\n", "");

    for (const auto& s : dados) {
        auto r = fmt::format("{:<4} | {:#<10X} | {:>12.4f} | {:>15.3f} | {:>20.3e}",
                             s.id, s.raw, s.tensao, s.temp, s.temp);
        std::cout << r << "\n";
    }
    std::cout << fmt::format("{:─^80}\n", "");

    return 0;
}

  1. Configuração de formatação encapsulada

Estrutura que agrupa parâmetros e aplica a formatação.

#include <fmt/format.h>
#include <iostream>

struct ConfigFormato {
    int largura;
    int precisao;
    bool maiusculo;
    bool prefixo;
    char alinhamento;

    std::string formatInt(int valor) const {
        return fmt::format("{:{}{}d}", valor, alinhamento, largura);
    }

    std::string formatHex(unsigned valor) const {
        std::string f = "{:";
        f += alinhamento;
        if (prefixo) f += "#";
        f += std::to_string(largura);
        f += maiusculo ? "X}" : "x}";
        return fmt::format(fmt::runtime(f), valor);
    }

    std::string formatCientifica(double valor) const {
        return fmt::format("{:{}{}.{}e}", valor, alinhamento, largura, precisao);
    }
};

int main() {
    ConfigFormato cfg1 {10, 3, true, true, '>'};
    ConfigFormato cfg2 {8, 6, false, false, '<'};

    std::cout << "cfg1:\n";
    std::cout << "int: " << cfg1.formatInt(42) << "\n";
    std::cout << "hex: " << cfg1.formatHex(0xBEEF) << "\n";
    std::cout << "cient: " << cfg1.formatCientifica(12345.6789) << "\n";

    std::cout << "\ncfg2:\n";
    std::cout << "int: " << cfg2.formatInt(-200) << "\n";
    std::cout << "hex: " << cfg2.formatHex(0xABCD) << "\n";
    std::cout << "cient: " << cfg2.formatCientifica(12345.6789) << "\n";

    return 0;
}

Esses exemplos mostram como fmt::format oferece controle fino sobre a representação textual de dados numéricos e textuais, sendo amplamente utilizada em conjunto com spdlog e outras bibliotecas C++ modernas.

Tags: fmt spdlog C++ formatação hexadecimal

Publicado em 7-3 23:26