21
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Go】書式指定子をまとめる

Last updated at Posted at 2023-05-09

はじめに

こんにちは、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にあげてるので興味のある方は触ってみてください。

間違いなどありましたら、コメントいただけると幸いです。最後まで読んでいただきありがとうございました。

21
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?