fmt パッケージには表示関数のファミリーが3つあり、どのファミリーにも同じ3つの種類があります。
| 関数 | 出力先 | 書式 |
|---|---|---|
Print、Println、Printf | 標準出力 | デフォルト、空白と改行付きのデフォルト、書式文字列 |
Sprint、Sprintln、Sprintf | 戻り値の string | 同じ3つのスタイル |
Fprint、Fprintln、Fprintf | 任意の io.Writer(ファイル、バッファ、HTTPレスポンス) | 同じ3つのスタイル |
Errorf | 戻り値の error | 書式文字列、加えてラップ用の %w |
Println はオペランドの間に空白を入れ、最後に改行を付けます。Printf は何も付けないので、\n を自分で書きます。Print は、両側とも文字列でないオペランドの間にだけ空白を入れます。これはかなり意外な挙動なので、ほとんどのコードは Println か Printf を使います。
書式指定子
汎用
| 指定子 | 表示するもの | 出力例 |
|---|---|---|
%v | デフォルトの形式の値 | {Ana 31 [admin]} |
%+v | フィールド名付きの構造体 | {Name:Ana Age:31 Tags:[admin]} |
%#v | 値のGo構文表現 | main.User{Name:"Ana", Age:31, Tags:[]string{"admin"}} |
%T | 型 | main.User |
%% | パーセント記号そのもの | % |
デバッグのときに使うのは %+v です。構造体へのポインタはアドレスではなく &{...} として表示されます。マップはキーをソートして表示するので、マップの反復順がランダムでも出力は安定します。
整数
| 指定子 | 意味 | fmt.Sprintf(verb, 255) |
|---|---|---|
%d | 10進数 | 255 |
%b | 2進数 | 11111111 |
%o | 8進数 | 377 |
%O | 0o 接頭辞付きの8進数 | 0o377 |
%x / %X | 16進数(小文字または大文字) | ff / FF |
%#x | 0x 接頭辞付きの16進数 | 0xff |
%c | そのコードポイントの文字 | ÿ |
%q | クォート付きの文字リテラル | 'ÿ' |
%U | Unicode形式 | U+00FF |
浮動小数点数
| 指定子 | 意味 | fmt.Sprintf(verb, 1234.5678) |
|---|---|---|
%f | 10進数、デフォルトは小数点以下6桁 | 1234.567800 |
%.2f | 10進数、小数点以下2桁 | 1234.57 |
%e | 指数表記 | 1.234568e+03 |
%g | %e か %f の短いほう、末尾のゼロなし | 1234.5678 |
%v | %g と同じ | 1234.5678 |
%.2f は浮動小数点数の正確な2進数の値を丸めるので、fmt.Sprintf("%.2f", 2.675) は 2.67 になります。2.675に最も近いfloat64は2.675よりわずかに小さいからです。お金を浮動小数点数から整形してはいけません。セントを整数で持ちます。
文字列とバイト
| 指定子 | 意味 | fmt.Sprintf(verb, "go\n") |
|---|---|---|
%s | 文字列そのもの | go と改行 |
%q | ダブルクォート付き、エスケープを表示 | "go\n" |
%x | 各バイトの16進数 | 676f0a |
% x | 空白区切りの16進数 | 67 6f 0a |
[]byte に %s を使うとテキストとして表示し、%v を使うと数値([104 105])を表示します。
その他の型
| 指定子 | 型 | 表示するもの |
|---|---|---|
%t | bool | true または false |
%p | ポインタ、スライス、マップ、チャネル、関数 | 0xc000012345 のようなアドレス |
%w | error(Errorf でのみ) | エラーのメッセージ、さらにラップする |
幅、精度、パディング
% と指定子の間に、フラグ、幅、精度を置けます。
| 形 | 効果 |
|---|---|
%5d | 幅5、右寄せ(左側を空白で埋める) |
%-5d | 幅5、左寄せ |
%05d | ゼロで埋める |
%.2f | 小数点以下2桁 |
%8.2f | 幅8で小数点以下2桁 |
%.3s | 文字列の最大3文字 |
%+d | 常に符号を表示する |
%*d | 幅を次の引数から取る |
2.5 を %.0f で整形すると 2 になることに注意してください。ここでは0.5を偶数方向に丸めます。文字列の幅は表示上の桁数ではなくルーン数で数えるので、日本語などのCJK文字や絵文字では表の列がずれることがあります。可変長のテキストの列をそろえるなら、text/tabwriter が幅を測ってくれます。
引数のインデックス
%[n] は位置で引数を選ぶので、同じ引数を再利用できます。
fmt.Printf("%[2]s %[1]s\n", "world", "hello") // hello world
fmt.Printf("%d %[1]x %[1]b\n", 10) // 10 a 1010
Errorfと%w
fmt.Errorf は書式文字列から error を作ります。%w を使うと別のエラーもラップするので、呼び出し側は元のエラーを検出できます。
呼び出し側が原因をチェックする必要がありそうなら %w を、意図的に隠すなら %v を使います。Go 1.20以降は、1つの Errorf 呼び出しに複数の %w を含められます。ラップについてはエラー処理のページで詳しく扱っています。
String()による独自の書式
String() string メソッドを持つ型は、%v、%s、Println での表示方法を自分で決められます。
%d は String() を通らず、基底の数値を表示します。エラー型での同等のメソッドは Error() string で、String() より優先されます。
指定子が間違っているとき
fmt は書式が間違っていてもpanicしません。問題をその場に表示します。
fmt.Printf("%d\n", "oops")
fmt.Printf("%d %d\n", 1)
fmt.Printf("%d\n", 1, 2)
%!d(string=oops)
1 %!d(MISSING)
1
%!(EXTRA int=2)
何もクラッシュさせないので、この出力は本番環境まで届きがちです。go vet はビルド時に3つとも検出します。
./main.go:8:2: fmt.Printf format %d has arg "oops" of wrong type string
./main.go:9:2: fmt.Printf format %d reads arg #2, but call has 1 arg
./main.go:10:2: fmt.Printf call needs 1 arg but has 2 args
パフォーマンスについて
fmt はすべての引数を any として受け取り、実行時に型を調べます(構造体、スライス、マップではリフレクションを使います)。ログや出力には問題ありませんが、タイトなループでは差が出ます。数値を1つ変換するだけなら、strconv.Itoa や strconv.FormatFloat のほうが Sprintf より高速です。ループで長い文字列を組み立てるなら、Sprintf の結果を連結するのではなく、fmt.Fprintf(&b, ...) で strings.Builder に書き込みます。
よくある質問
GoのPrintln、Printf、Sprintfの違いは何ですか?
fmt.Println は引数を空白で区切り、最後に改行を付けて表示します。fmt.Printf は書式文字列に従って表示し、改行は付けません。fmt.Sprintf は Printf と同じように整形しますが、表示せずに結果を文字列として返します。fmt.Errorf も同じことをして error を返します。
Goで構造体をフィールド名付きで表示するには?
%+v を使います:fmt.Printf("%+v\n", user) は {Name:Ana Age:31} と表示します。%v は値だけの {Ana 31} を、%#v は型を含むGoの構文 main.User{Name:"Ana", Age:31} を表示します。
Goで浮動小数点数を小数点以下2桁に整形するには?
%.2f を使います:fmt.Sprintf("%.2f", 3.14159) は "3.14" を返します。列をそろえるなら幅を加えて %8.2f、左寄せならマイナス記号を付けて %-8.2f とします。strconv.FormatFloat(f, 'f', 2, 64) でも書式文字列なしで同じ結果が得られます。
fmt.Errorfの%wは何をするのですか?
%w はエラーを %v と同じように整形し、さらにそれをラップするので、新しいエラーが元のエラーを持ち運びます。その後 errors.Is と errors.As でラップされたエラーを見つけられます:err := fmt.Errorf("load config: %w", os.ErrNotExist) なら errors.Is(err, os.ErrNotExist) はtrueです。%w は fmt.Errorf でしか使えません。
なぜ出力に%!d(string=...)と表示されるのですか?
書式指定子が引数の型と合っていません。たとえば %d に文字列を渡した場合です。fmt はpanicせずに、問題をその場に表示します:%!d(string=oops)。引数が足りないと %!d(MISSING)、多すぎると %!(EXTRA int=2) と表示されます。go vet はプログラムを実行する前に3つとも検出します。