はじめに
こんにちは、kenです。
この記事ではGo言語の書式指定子についてまとめていきます。書式指定子とは
fmt.Printf("こんにちは、私は%sです\n", "ken")
に出てくる%s
のようなもののことです。
調べてみると思ったより種類があったので、自分の勉強も兼ねてまとめてみました。
以下の内容は次のfmtパッケージの公式ドキュメントの内容に準拠しています。
General
型を問わずどれにでも使える書式指定子は以下です。
書式指定子 | 説明 |
---|---|
%v |
デフォルトのフォーマット |
%+v |
デフォルトのフォーマット(構造体にはフィールド名つき) |
%#v |
Goの構文に合わせたフォーマット |
%T |
型(type)をGoの構文で出力 |
%% |
「%」を出力 |
例
package format
import "fmt"
type Person struct {
name string
age int
}
func PrintTitle(title string) {
fmt.Println("----------------------------------------")
fmt.Printf("%s\n", title)
fmt.Println("----------------------------------------")
}
func General() {
var (
i int = 0
f float64 = 0.5
flag bool = false
str string = "Hello"
arr [3]string = [3]string{"a", "b", "c"}
person Person = Person{
name: "ken",
age: 23,
}
m map[string]int = map[string]int{"one": 1, "two": 2}
)
var variables []interface{} = []interface{}{
i, f, flag, str, arr, person, m,
}
PrintTitle("%v - デフォルトフォーマット")
for _, v := range variables {
fmt.Printf("%v\n", v)
}
//----------------------------------------
// %v - デフォルトフォーマット
// ----------------------------------------
// 0
// 0.5
// false
// Hello
// [a b c]
// {ken 23}
// map[one:1 two:2]
PrintTitle("%+v - デフォルトフォーマット(構造体にはフィールド名つき) ")
for _, v := range variables {
fmt.Printf("%+v\n", v)
}
//----------------------------------------
// %+v - デフォルトフォーマット(フィールド名つき)
// ----------------------------------------
// 0
// 0.5
// false
// Hello
// [a b c]
// {name:ken age:23}
// map[one:1 two:2]
PrintTitle("%#v - Goの構文に合わせたフォーマット")
for _, v := range variables {
fmt.Printf("%#v\n", v)
}
//----------------------------------------
// %#v - Goの構文に合わせたフォーマット
// ----------------------------------------
// 0
// 0.5
// false
// "Hello"
// [3]string{"a", "b", "c"}
// format.Person{name:"ken", age:23}
// map[string]int{"one":1, "two":2}
PrintTitle("%T - 型(type)をGoの構文で出力")
for _, v := range variables {
fmt.Printf("%T\n", v)
}
// ----------------------------------------
// %T - typeを出力
// ----------------------------------------
// int
// float64
// bool
// string
// [3]string
// format.Person
// map[string]int
PrintTitle("%% - 「%」を出力")
fmt.Printf("%% ←これはパーセント\n")
// ----------------------------------------
// %% - 「%」を出力
// ----------------------------------------
// % ←これはパーセント
}
デフォルトフォーマット%v
について
デフォルトフォーマットとはなんぞやと思われた方もいらっしゃるかもしれませんが、それぞれの型について次の書式指定子を指定したことになるみたいです。
型 | %vに対応する書式指定子 |
---|---|
bool | %t |
int,int8 etc | %d |
uint, uint8 etc. |
%d (%#v のときは%#x ) |
float32, complex64, etc | %g |
string | %s |
chan | %p |
pointer | %p |
ブール値
書式指定子 | 説明 |
---|---|
%t |
trueまたはfalseで出力 |
例
func Boolean() {
var T bool = true
var F bool = false
PrintTitle("%t - trueまたはfalseで出力")
fmt.Printf("%t,%t\n", T, F)
// ----------------------------------------
// %t - trueまたはfalseで出力
// ----------------------------------------
// true,false
}
整数
書式指定子 | 説明 |
---|---|
%b |
2進表記 |
%c |
対応するUnicodeコードポイントによって表される文字 |
%d |
10進表記 |
%o |
8進表記 |
%O |
0o のprefixがついた8進表記 |
%q |
Go構文で安全にエスケープされた単一引用符の文字リテラル。 |
%x |
16進表記(a-fの文字は小文字表記) |
%X |
16進表記(A-Fの文字は大文字表記) |
%U |
Unicodeフォーマット |
例
func Integer() {
var num int = 110
PrintTitle("%b - 2進表記")
fmt.Printf("%b\n", num)
// ----------------------------------------
// %b - 2進表記
// ----------------------------------------
// 1101110
PrintTitle("%c - 対応するUnicodeコードポイントによって表される文字")
fmt.Printf("%c\n", num)
// ----------------------------------------
// %c - 対応するUnicodeコードポイントによって表される文字
// ----------------------------------------
// n
PrintTitle("%d - 10進表記")
fmt.Printf("%d\n", num)
// ----------------------------------------
// %d - 10進表記
// ----------------------------------------
// 110
PrintTitle("%o - 8進表記")
fmt.Printf("%o\n", num)
// ----------------------------------------
// %o - 8進表記
// ----------------------------------------
// 156
PrintTitle("%O - `0o`のprefixがついた8進表記")
fmt.Printf("%O\n", num)
// ----------------------------------------
// %O - `0o`のprefixがついた8進表記
// ----------------------------------------
// 0o156
PrintTitle("%q - Go構文で安全にエスケープされた単一引用符の文字リテラル")
fmt.Printf("%q\n", num)
// ----------------------------------------
// %q - Go構文で安全にエスケープされた単一引用符の文字リテラル
// ----------------------------------------
// 'n'
PrintTitle("%x - 16進表記(a-fの文字は小文字表記)")
fmt.Printf("%x\n", num)
// ----------------------------------------
// %x - 16進表記(a-fの文字は小文字表記)
// ----------------------------------------
// 6e
PrintTitle("%X - 16進表記(A-Fの文字は大文字表記)")
fmt.Printf("%X\n", num)
// ----------------------------------------
// %X - 16進表記(A-Fの文字は大文字表記)
// ----------------------------------------
// 6E
PrintTitle("%U - Unicodeフォーマット")
fmt.Printf("%U\n", num)
// ----------------------------------------
// %U - Unicodeフォーマット
// ----------------------------------------
// U+006E
}
補足
Unicodeコードポイント・Unicodeフォーマットとは
https://www.buildinsider.net/language/csharpunicode/01
まず、Unicodeで規定されている文字1つ1つには、最大で21bits(16進数で5~6桁)の数値が割り振られている。この数値をコードポイント(code point: 符号点、符号位置)という。
ちなみに、Unicodeでは、コードポイントの数値で文字を表すための表記として、「U+16進数」という書き方を使う。例えば、「a」であればU+61、「あ」であればU+3042と表記する(以下、文字コードは全て16進数で表記する)。
浮動小数点と複素数
書式指定子 | 説明 |
---|---|
%b |
符号付き十進法指数表記。指数は2のべき乗で、strconv.FormatFloatの'b'フォーマットと同様に表される。例:-123456p-78 |
%e |
指数表記。例:-1.234456e+78 |
%E |
eを大文字にした指数表記。例:-1.234456E+78 |
%f |
小数点表記、指数は使わない。例:123.456 |
%F |
%f の別名 |
%g |
指数が大きい場合は%e、そうでない場合は%f |
%G |
指数が大きい場合は%E、そうでない場合は%F |
%x |
16進数表記(指数は2のべき乗の10進数で表される)。例:-0x1.23abcp+20 |
%X |
大文字の16進数表記。例:-0X1.23ABCP+20 |
例
func FloatAndComplex() {
var f float64 = 0.5
var c complex128 = complex(3, 4) // 3+4i
var bigF float64 = 1234567.89
var bigC complex128 = complex(12345.678, 9876543.21) // 12345.678 + 9876543.21i
PrintTitle("%b - 符号付き指数表記。指数は2のべき乗で、strconv.FormatFloatの'b'フォーマットと同様に表される。例:-123456p-78")
fmt.Printf("float64: %b, complex: %b\n", f, c)
fmt.Printf("float64(Big): %b, complex(Big): %b\n", bigF, bigC)
// ----------------------------------------
// %b - 符号付き指数表記。指数は2のべき乗で、strconv.FormatFloatの'b'フォーマットと同様に表され る。例:-123456p-78
// ----------------------------------------
// float64: 4503599627370496p-53, complex: (6755399441055744p-51+4503599627370496p-50i)
// float64(Big): 5302428712241725p-32, complex(Big): (6787108256889176p-39+5302428760560108p-29i)
PrintTitle("%e - 指数表記。例:-1.234456e+78")
fmt.Printf("float64: %e, complex: %e\n", f, c)
fmt.Printf("float64(Big): %e, complex(Big): %e\n", bigF, bigC)
// ----------------------------------------
// %e - 指数表記。例:-1.234456e+78
// ----------------------------------------
// float64: 5.000000e-01, complex: (3.000000e+00+4.000000e+00i)
// float64(Big): 1.234568e+06, complex(Big): (1.234568e+04+9.876543e+06i)
PrintTitle("%E - eを大文字にした指数表記。例:-1.234456E+78")
fmt.Printf("float64: %E, complex: %E\n", f, c)
fmt.Printf("float64(Big): %E, complex(Big): %E\n", bigF, bigC)
// ----------------------------------------
// %E - eを大文字にした指数表記。例:-1.234456E+78
// ----------------------------------------
// float64: 5.000000E-01, complex: (3.000000E+00+4.000000E+00i)
// float64(Big): 1.234568E+06, complex(Big): (1.234568E+04+9.876543E+06i)
PrintTitle("%f - 小数点表記、指数は使わない。例:123.456")
fmt.Printf("float64: %f, complex: %f\n", f, c)
fmt.Printf("float64(Big): %f, complex(Big): %f\n", bigF, bigC)
// ----------------------------------------
// %f - 小数点表記、指数は使わない。例:123.456
// ----------------------------------------
// float64: 0.500000, complex: (3.000000+4.000000i)
// float64(Big): 1234567.890000, complex(Big): (12345.678000+9876543.210000i)
PrintTitle("%F - %fの別名")
fmt.Printf("float64: %F, complex: %F\n", f, c)
fmt.Printf("float64(Big): %F, complex(Big): %F\n", bigF, bigC)
// ----------------------------------------
// %F - %fの別名
// ----------------------------------------
// float64: 0.500000, complex: (3.000000+4.000000i)
// float64(Big): 1234567.890000, complex(Big): (12345.678000+9876543.210000i)
PrintTitle("%g - 指数が大きい場合は%e、そうでない場合は%f")
fmt.Printf("float64: %g, complex: %g\n", f, c)
fmt.Printf("float64(Big): %g, complex(Big): %g\n", bigF, bigC)
// ----------------------------------------
// %g - 指数が大きい場合は%e、そうでない場合は%f
// ----------------------------------------
// float64: 0.5, complex: (3+4i)
// float64(Big): 1.23456789e+06, complex(Big): (12345.678+9.87654321e+06i)
PrintTitle("%G - 指数が大きい場合は%E、そうでない場合は%F")
fmt.Printf("float64: %G, complex: %G\n", f, c)
fmt.Printf("float64(Big): %G, complex(Big): %G\n", bigF, bigC)
// ----------------------------------------
// %G - 指数が大きい場合は%E、そうでない場合は%F
// ----------------------------------------
// float64: 0.5, complex: (3+4i)
// float64(Big): 1.23456789E+06, complex(Big): (12345.678+9.87654321E+06i)
PrintTitle("%x - 16進数表記(指数は2のべき乗の10進数で表される)。例:-0x1.23abcp+20")
fmt.Printf("float64: %x, complex: %x\n", f, c)
fmt.Printf("float64(Big): %x, complex(Big): %x\n", bigF, bigC)
// ----------------------------------------
// %x - 16進数表記(指数は2のべき乗の10進数で表される)。例:-0x1.23abcp+20
// ----------------------------------------
// float64: 0x1p-01, complex: (0x1.8p+01+0x1p+02i)
// float64(Big): 0x1.2d687e3d70a3dp+20, complex(Big): (0x1.81cd6c8b43958p+13+0x1.2d687e6b851ecp+23i)
PrintTitle("%X - 大文字の16進数表記。例:-0X1.23ABCP+20")
fmt.Printf("float64: %X, complex: %X\n", f, c)
fmt.Printf("float64(Big): %X, complex(Big): %X\n", bigF, bigC)
// ----------------------------------------
// %X - 大文字の16進数表記。例:-0X1.23ABCP+20
// ----------------------------------------
// float64: 0X1P-01, complex: (0X1.8P+01+0X1P+02i)
// float64(Big): 0X1.2D687E3D70A3DP+20, complex(Big): (0X1.81CD6C8B43958P+13+0X1.2D687E6B851ECP+23i)
}
補足
%b - 符号付き指数表記について
float64
型であるf = 0.5
と%b
で出力された4503599627370496p-53
は$0.5 = 4503599627370496 × 2^{-53}$という対応をしています。
%x - 16進数表記(指数は2のべき乗の10進数で表される)について
complex128
型であるc = 3+4i
と%x
で出力された (0x1.8p+01+0x1p+02i)
がどう対応しているかについてみてみます。
まず実部から、、0x1.8p+01
を10進数に直してみます。16進数の$1.8$を10進数になおすと$1 \times 16^{0} + 8 \times 16^{-1} = 1.5$となるので0x1.8p+01
は$1.8_{(16)} \times 2^1 = 1.5 \times 2^1 = 3$となり、確かに実部の$3$が出てきています。
虚部に関しても同様で、16進数の $1$ は10進数でも $1$ なので、0x1p+02i
は$1_{(16)} \times 2^2 \times i= 1 \times 2^2 \times i= 4i$となります
文字列とバイトスライス
以下は、Go言語におけるフォーマット指定子に関する説明です。
フォーマット指定子 | 説明 |
---|---|
%s |
解釈される前の(加工されていないそのままの)表示。 |
%q |
Go構文で安全にエスケープされたダブルクォート囲みの文字列。 |
%x |
小文字を使う16進数表記。(1バイトにつき2文字) |
%X |
大文字を使う16進数表記。(1バイトにつき2文字) |
func StringAndSliceOfBytes() {
var str string = "Hello"
var sliceOfBytes []byte = []byte("abcd")
// = []byte{97, 98, 99, 100}
PrintTitle("%s - []byteとして解釈される前の(加工されていないそのままの)表示。")
fmt.Printf("string: %s, slice of bytes: %s\n", str, sliceOfBytes)
// ----------------------------------------
// %s - 解釈される前の(加工されていないそのままの)表示。
// ----------------------------------------
// string: Hello, slice of bytes: abcd
PrintTitle("%q - Go構文で安全にエスケープされたダブルクォート囲みの文字列。")
fmt.Printf("string: %q, slice of bytes: %q\n", str, sliceOfBytes)
// ----------------------------------------
// %q - Go構文で安全にエスケープされたダブルクォート囲みの文字列。
// ----------------------------------------
// string: "Hello", slice of bytes: "abcd"
PrintTitle("%x - 小文字を使う16進数表記。(1バイトにつき2文字)")
fmt.Printf("string: %x, slice of bytes: %x\n", str, sliceOfBytes)
// ----------------------------------------
// %x - 小文字を使う16進数表記。1バイトにつき2文字。
// ----------------------------------------
// string: 48656c6c6f, slice of bytes: 61626364
PrintTitle("%X - 大文字を使う16進数表記。(1バイトにつき2文字)")
fmt.Printf("string: %X, slice of bytes: %X\n", str, sliceOfBytes)
// ----------------------------------------
// %X - 大文字を使う16進数表記。(1バイトにつき2文字)
// ----------------------------------------
// string: 48656C6C6F, slice of bytes: 61626364
// ----------------------------------------
}
補足
%x - 小文字を使う16進数表記。(1バイトにつき2文字)について
sliceOfBytes []byte = []byte("abcd")
が'%x'ではなぜ61626364
と出力されたのかについて説明します。
"abcd"
を[]byte
で表すと[]byte{97, 98, 99, 100}
です。これはa,b,c,dのそれぞれのコードポイントが97,98,99,100であることに対応しています。この4つの数字を16進数になおすと61,62,63,64となります。これらを繋げたものが%x
で出力された61626364
というわけです。
スライス
以下は、Go言語におけるフォーマット指定子に関する説明です。
フォーマット指定子 | 説明 |
---|---|
%p |
スライスの先頭要素のアドレスを16進数表記で表示。先頭に0xが付く。 |
例
func Slice() {
var slice []string = []string{"Hello", "World"}
PrintTitle("%p - スライスの先頭要素のアドレスを16進数表記で表示。先頭に0xが付く。")
fmt.Printf("%p\n", slice)
fmt.Printf("変数sliceの先頭要素のアドレスは%#p\n", &slice[0])
// ----------------------------------------
// %p - スライスの先頭要素のアドレスを16進数表記で表示。先頭に0xが付く。
// ----------------------------------------
// 0xc00011a000
// 変数sliceの先頭要素のアドレスはc00011a000
}
ポインタ
以下は、Go言語におけるフォーマット指定子に関する説明です。
フォーマット指定子 | 説明 |
---|---|
%p |
アドレスを16進数表記で表示。先頭に0xが付く。 |
%b |
アドレスを符号付き2進表記で表示 |
%d |
アドレスを10進表記で表示 |
%o |
アドレスを8進表記で表示 |
%x |
アドレスを小文字を使う16進表記で表示 |
%X |
アドレスを大文字を使う16進表記で表示 |
例
func Pointer() {
var num int = -123
var ptr *int = &num
PrintTitle("%p - アドレスを16進数表記で表示。先頭に0xが付く。")
fmt.Printf("%p\n", ptr)
// ----------------------------------------
// %p - アドレスを16進数表記で表示。先頭に0xが付く。
// ----------------------------------------
// 0xc000016168
PrintTitle("%b - アドレスを符号付き2進表記で表示")
// fmt.Printf("%b\n", ptr)
// ----------------------------------------
// %b - アドレスを符号付き2進表記で表示
// ----------------------------------------
// 1100000000000000000000010110000101101000
PrintTitle("%d - アドレスを10進表記で表示")
fmt.Printf("%d\n", ptr)
// ----------------------------------------
// %d - アドレスを10進表記で表示
// ----------------------------------------
// 824633811304
PrintTitle("%o - アドレスを8進表記で表示")
fmt.Printf("%o\n", ptr)
// ----------------------------------------
// %o - アドレスを8進表記で表示
// ----------------------------------------
// 14000000260550
PrintTitle("%x - アドレスを小文字を使う16進表記で表示")
fmt.Printf("%x\n", ptr)
// ----------------------------------------
// %x - アドレスを小文字を使う16進表記で表示
// ----------------------------------------
// c000016168
PrintTitle("%X - アドレスを大文字を使う16進表記で表示")
fmt.Printf("%X\n", ptr)
// ----------------------------------------
// %X - アドレスを大文字を使う16進表記で表示
// ----------------------------------------
// C000016168
}
(おまけ)エラー型
これはfmt.Errorf()の説明に書いてあることなのですが、エラーをラップするときは%w
という書式指定子を使うことができます。
func Error() {
Error := errors.New("an error occurred")
WrapError := fmt.Errorf("wrap!!: %w", Error)
PrintTitle("%w - エラーのラップ")
fmt.Println(WrapError)
// ----------------------------------------
// %w - エラーのラップ
// ----------------------------------------
// wrap!!: an error occurred
}
おわりに
改めてみるとあまり使ったことのない書式指定子があったので驚きました。(普段は%v
とかばっかり使うので……)
今回紹介したコードはGitHubにあげてるので興味のある方は触ってみてください。
間違いなどありましたら、コメントいただけると幸いです。最後まで読んでいただきありがとうございました。