← Back to Blog
C++C++20
聊聊 C++20 的 std::format
排版字串時,比起 printf 的 % 或 iostream 的 <<,std::format 的格式跟參數分離得比較清楚, 也比較好讀。這篇先當速查筆記,整理幾個最常需要查的點:浮點數的 precision / type,以及 precision 用在字串上會截斷的坑。
基本用法
#include <format>,用 {} 當佔位符。 它是 type-safe 的,不用像 printf 那樣手動對 %d、%f。
cpp
#include <format>#include <print> // C++23
std::string s = std::format("{} + {} = {}", 1, 2, 3);// "1 + 2 = 3"
std::println("{} + {} = {}", 1, 2, 3); // C++23,直接印想重複用同一個參數,就標位置 {0}:
cpp
std::format("{0}{1}{0}", "ab", "cd"); // "abcdab"對齊、寬度、符號
format spec 看起來很長,但日常大多只會用到 width、precision、type 這幾個:
text
{:[fill][align][sign][#][0][width][.precision][type]}< 靠左、> 靠右、^ 置中:
cpp
std::format("{:>8}", "hi"); // " hi"std::format("{:<8}", "hi"); // "hi "std::format("{:^8}", "hi"); // " hi "std::format("{:*^8}", "hi"); // "***hi***"std::format("{:08}", 42); // "00000042" 0 + width 補零std::format("{:+}", 42); // "+42" 正數也加號std::format("{: }", 42); // " 42" 正數留空位,負數照樣 -浮點數:precision 跟 type
浮點數先記兩件事就夠:precision(.N) 決定精度、type(最後那個字母)決定顯示形式。
cpp
double pi = 3.14159265;
std::format("{:.2f}", pi); // "3.14" 固定小數 2 位std::format("{:.3e}", pi); // "3.142e+00" 科學記號std::format("{:.4g}", pi); // "3.142" g:自動選 f/e,緊湊顯示金額、平均值、比例這類輸出,{:.2f} 通常就很合適:
cpp
for (double price : {12.5, 8.99, 159.0, 68.75}) std::println("${:>8.2f}", price);// $ 12.50// $ 8.99// $ 159.00// $ 68.75坑:precision 在字串上是「截斷長度」
同一個 .N,放在浮點數跟字串上不是同一件事:
- 浮點數:精度,可能是小數位或有效位數,取決於 type
- 字串:截斷長度,最多取幾個字元
cpp
std::format("{:.3}", 3.14159); // "3.14" 3 位有效數字std::format("{:.3}", "formatting"); // "for" 截到 3 個字std::format("{:.3f}", 3.14159); // "3.142" f 才是 3 位小數所以 {:.3} 套在字串上不會報錯。 這種 bug 特別煩:不會炸,只是輸出悄悄變短。
整數:進制與前綴
cpp
std::format("{:b}", 42); // "101010"std::format("{:o}", 42); // "52"std::format("{:x}", 255); // "ff"std::format("{:#x}", 255); // "0xff" # 加上進制前綴std::format("{:#010b}", 42); // "0b00101010"寬度、精度也能動態傳
不想寫死,就用巢狀的 {} 把寬度或精度當參數傳進去:
cpp
int w = 8;std::format("{:>{}}", x, w); // 寬度由 w 決定
int n = 3;std::format("{:.{}f}", pi, n); // 小數位數由 n 決定這招很適合用在 log / table output:欄寬固定, 但寬度或小數位可以從 config 來。