Skip to content
C++

String Formatting Quick Reference

C++ string formatting cheat sheet — std::format, std::print, format specifiers, custom formatters, and migration from printf/sprintf/ostringstream.

Quick Start

cpp
#include <format>   // C++20
#include <print>    // C++23

std::string s = std::format("Hello, {}! x={}", name, 42);
std::println("result: {:.2f}", 3.14159);  // to stdout with newline
std::print(stderr, "Error: {}\n", msg);   // to file

Format Specifiers

cpp
{[arg_id][:[fill][align][sign][#][0][width][.precision][type]]}

Width and Alignment

cpp
std::format("{:10}",  "hello")    // "hello     "  left-aligned (default for strings)
std::format("{:<10}", "hello")    // "hello     "  left
std::format("{:>10}", "hello")    // "     hello"  right
std::format("{:^10}", "hello")    // "  hello   "  center
std::format("{:*^10}", "hello")   // "**hello***"  center with fill char '*'
std::format("{:10}", 42)          // "        42"  right-aligned (default for integers)
std::format("{:<10}", 42)         // "42        "

Integer Types

cpp
std::format("{}", 255)            // "255"      decimal (default)
std::format("{:d}", 255)          // "255"      decimal explicit
std::format("{:x}", 255)          // "ff"       hex lowercase
std::format("{:X}", 255)          // "FF"       hex uppercase
std::format("{:o}", 255)          // "377"      octal
std::format("{:b}", 255)          // "11111111" binary
std::format("{:#x}", 255)         // "0xff"     hex with prefix
std::format("{:#b}", 8)           // "0b1000"   binary with prefix
std::format("{:08x}", 255)        // "000000ff" zero-padded width 8
std::format("{:+}", 42)           // "+42"      force sign
std::format("{: }", 42)           // " 42"      space for positive

Floating Point

cpp
std::format("{}", 3.14)           // "3.14"
std::format("{:.2f}", 3.14159)    // "3.14"     fixed, 2 decimal places
std::format("{:.4e}", 3.14159)    // "3.1416e+00" scientific notation
std::format("{:.4g}", 3.14159)    // "3.142"    shortest of f or e
std::format("{:.4a}", 1.0)        // "0x1.0000p+0" hex float
std::format("{:10.2f}", 3.14)     // "      3.14" width + precision
std::format("{:+.2f}", 3.14)      // "+3.14"    force sign
std::format("{:010.2f}", 3.14)    // "0000003.14" zero-padded

Strings

cpp
std::format("{}", "hello")        // "hello"
std::format("{:10}", "hello")     // "hello     "  padded to 10
std::format("{:.3}", "hello")     // "hel"         max 3 chars
std::format("{:?}", "he\nllo")    // "\"he\\nllo\"" debug/escaped (C++23)

Booleans

cpp
std::format("{}", true)           // "true"
std::format("{:d}", true)         // "1"      as integer
std::format("{:s}", true)         // "true"   explicit string

Positional and Named Arguments

cpp
// Positional (0-indexed)
std::format("{0} {1} {0}", "a", "b")   // "a b a"

// Cannot mix positional and auto-numbered in one format string

std::print / std::println (C++23)

cpp
#include <print>

std::print("no newline: {}", x);
std::println("with newline: {}", x);       // appends \n

std::println(stderr, "error: {}", msg);    // to stderr
std::println(file, "record: {}", data);    // to FILE* or ostream

Formatting to a Buffer

cpp
// Format into a string
std::string s = std::format("{} + {} = {}", 1, 2, 3);

// Format into existing buffer (avoids allocation)
std::string buf;
buf.reserve(256);
std::format_to(std::back_inserter(buf), "{:08x}", 0xDEAD);

// With size limit
char arr[32];
auto result = std::format_to_n(arr, sizeof(arr) - 1, "{}", value);
arr[result.size] = '\0';

// Count required chars (no output)
size_t needed = std::formatted_size("{:.2f}", 3.14159);

Custom Formatter

cpp
struct Point { double x, y; };

template<>
struct std::formatter<Point> {
    // Parse format spec (e.g., "{:.2f}")
    double precision = -1;
    constexpr auto parse(std::format_parse_context& ctx) {
        auto it = ctx.begin();
        // optionally parse custom specs from [it, ctx.end())
        return it;  // return iterator past parsed spec
    }

    // Format the value
    auto format(const Point& p, std::format_context& ctx) const {
        return std::format_to(ctx.out(), "({}, {})", p.x, p.y);
    }
};

std::println("point: {}", Point{1.5, 2.7});  // "point: (1.5, 2.7)"

Reusing existing formatters

cpp
template<>
struct std::formatter<Color> : std::formatter<std::string_view> {
    auto format(Color c, std::format_context& ctx) const {
        std::string_view name = "unknown";
        switch (c) {
        case Color::Red:   name = "Red";   break;
        case Color::Green: name = "Green"; break;
        case Color::Blue:  name = "Blue";  break;
        }
        return std::formatter<std::string_view>::format(name, ctx);
    }
};

Migration Guide

From printf

cpp
// printf
printf("x=%d, y=%.2f, s=%s\n", x, y, s.c_str());

// std::format
std::println("x={}, y={:.2f}, s={}", x, y, s);

From sprintf

cpp
// sprintf (unsafe — buffer overflow risk)
char buf[256];
sprintf(buf, "%08x", value);

// std::format (safe)
std::string s = std::format("{:08x}", value);

From ostringstream

cpp
// ostringstream
std::ostringstream oss;
oss << std::setw(10) << std::setprecision(2) << std::fixed << x;
std::string s = oss.str();

// std::format
std::string s = std::format("{:10.2f}", x);

From the {fmt} library

cpp
// {fmt} (same syntax — std::format is based on it)
fmt::format("hello {}", name);
fmt::print("result: {:.2f}", x);

// std::format (drop-in for most cases)
std::format("hello {}", name);
std::print("result: {:.2f}", x);

Common Patterns

cpp
// Hex dump
for (size_t i = 0; i < data.size(); ++i) {
    std::print("{:02x}", data[i]);
    if ((i + 1) % 16 == 0) std::println();
    else if ((i + 1) % 8 == 0) std::print("  ");
    else std::print(" ");
}

// Table formatting
std::println("{:<20} {:>10} {:>8}", "Name", "Score", "Rank");
std::println("{:-<20} {:->10} {:->8}", "", "", "");
for (auto& [name, score, rank] : results)
    std::println("{:<20} {:>10} {:>8}", name, score, rank);

// Timestamp
auto now = std::chrono::system_clock::now();
std::println("{:%Y-%m-%d %H:%M:%S}", now);  // 2025-01-15 14:30:00
Edit on GitHub