Go の fmtパッケージ と決着をつける

35613 ワード

import "fmt"

fmtパッケージは,フォーマット処理を含めた入出力のための機能がまとめられたパッケージです

大きく分けて以下の3つのグループに分類できるという話

📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣
fmtパッケージは,

  • 名前が「P」から始まる関数のグループ...①
  • 名前が「S」から始まる関数のグループ...②
  • 名前が「F」から始まる関数のグループ...③

の3つのグループに分類することができます!
📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣 📣

それぞれ具体的には以下の用途があります(↓)

  1. 標準出力への出力関数[1]...①
    • fmt.Print(a ...interface{}) (n intなど, err error)
    • fmt.Printf(format string, a ...interface{}) (n int, err error)
    • fmt.Println(a ...interface{}) (n intなど, err error)
  2. 文字列への出力(生成)関数...②
    • fmt.Sprint(a ...interface{}) string
    • fmt.Sprintf(format string, a ...interface{}) string
    • fmt.Sprintln(a ...interface{}) string
  3. ファイル[2]への出力関数[1:1]...③
    • fmt.Fprint(w io.Writer, a ...interface{}) (n intなど, err error)
    • fmt.Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
    • fmt.Fprintln(w io.Writer, a ...interface{}) (n intなど, err error)

この分類の意味を理解してしまえば,もう「どの関数使えばいいんや...」「あれ,この場合はどれがいいんだっけ...」という事態に陥らずに済みます!

それぞれ解説していきます.

まず最初に ※

全グループに共通して存在する,名前の末尾が「f」で終わる関数は「フォーマット付き」であることを表しています

💡 使い方は,第1引数に文字列でフォーマットを指定して,第2引数以降は埋め込まれる任意のデータをinterface{}型として並べます

フォーマット文字列の中では,「%d」などのような書式指定子と呼ばれる記号を使用して,データの埋め込む場所とその書式(型)を指定します

開発にはもちろん,デバッグなどにも非常に役立ちます.

// 1. 標準出力へフォーマットした文字列を出力(①グループ)
name := Tom
fmt.Printf("My name is %s.", name)
// 出力
"My name is Tom."


// 2. 文字列としてフォーマットした文字列を出力(生成)(②グループ)
// あくまで文字列を「生成」するための関数であるため,実際はデータを変数などに格納するなどして使います
word := "Go言語"
s := fmt.Spintf("I love %s.\n", word)
// s == "I love Go言語."


// 3. ファイルへフォーマットした文字列を出力を指定(③グループ)
f, err := os.Create("foo.text")  // 「foo.text」というファイルを作成し変数に格納
if err != nil {
	log.Fatal(err)  // エラーハンドリング
}
defer f.Close()

fmt.Fprintf(f, "%s!\n", "hello")
// foo.textの内容
// hello!

また,文字だけでなく数値や浮動小数点数などのデータを整形して出力できます(↓)

// 1. 標準出力へフォーマットした文字列を出力(①グループ)
number := 4
fmt.Printf("%d * %d = %d\n", number, number, number*number)
// 出力
"4 * 4 = 16"


// 2. 文字列としてフォーマットした文字列を出力(生成)(②グループ)
// あくまで文字列を「生成」するための関数であるため,実際はデータを変数などに格納するなどして使います
s := fmt.Spintf("%.2f\n", 1.23456)
// s == "1.23"


// 3. ファイルへフォーマットした文字列を出力を指定(③グループ)
f, err := os.Create("foo.text")  // 「foo.text」というファイルを作成し変数に格納
if err != nil {
	log.Fatal(err)  // エラーハンドリング
}
defer f.Close()

fmt.Fprintf(f, "%05d|%05d\n", 121, 33)
// foo.textの内容
// 00121|00033

それぞれの書式指定子(%dなど)の意味はこちらです(↓)

書式指定子一覧

文字列型

書式指定子 出力 補足
%s "Go言語" "Go言語"
%10s "Go言語" "          Go言語" 文字数10になるように右詰め
%-10s "Go言語" "Go言語          " 文字数10になるように左詰め
%q "Go言語" ""Go言語"" ダブルクオート付き
%x "Go言語" "476fe8a880e8aa9e" 16進数(a-f小文字)
%X "Go言語" "476FE8A880E8AA9E" 16進数(a-f大文字)

整数型・浮動小数点型

書式指定子 出力 補足
%d -1 "-1" 整数を表示
%+d 1 "+1" 符号を表示
%5d 123 "  123" 指定した桁数で右詰め
%-5d 123 "123  " 指定した桁数で左詰め
%05d 123 "00123" 指定した桁数で右詰めし,0で埋める
%c 65 "A" Unicode文字
%q 36938 '遊' シングルクオートで囲われたUnicode文字
%o 100 "144" 8進数
%#o 100 "0144" 0付き8進数
%x 10203 "27db" 16進数(a-f小文字)
%X 10203 "27DB" 16進数(A-F大文字)
%#x 10203 "0x27db" 0x付き16進数(a-f小文字)
%#X 10203 "0x27DB" 0x付き16進数(A-F大文字)
%U 36938 "U+904A" Unicodeコードポイント
%#U 36938 "U+904A '遊'" Unicodeコードポイントと文字
%b 250 "11111010" 2進数
%f 123.456 "123.456000" 実数表現
%.2f 123.456 "123.45" 小数点以下を2桁に丸める

その他

書式指定子 出力 補足
%t true true bool型専用
%p 任意のポインタ型 (例)"0xc82000a380 ポインタのアドレス16進数
%T 任意の型 (例)"int" Goの型を表示

%v

%vはGoのさまざまな型の値を柔軟に出力できる,トランプで言うJokerみたいな書式指定子です
マップや配列,スライスやチャネルなどについても%vは有効に作用します

書式指定子 出力 補足
%v interface{} 何でも受け付けます

さて,ここからは上記の3つのグループについて説明します.

標準出力への出力関数(Pから始まるグループ)

名前が「P」から始まる関数のグループは,通常の標準出力を行います

fmt.Print

interface{}型の任意の数の引数をとり,さまざまなデータ型を末尾の改行無しで出力します
データ同士を「,(カンマ)」で区切って複数出力することも可能ですが,文字列型と隣接するデータは間にスペースが付きません

// どんな型でもそのまま出力
fmt.Print("アイウエオ")  // => アイウエオ
fmt.Print(struct{ X, Y int }{ 1, 2 })  // => { 1, 2 }
fmt.Print(1, 123, "Go言語", struct{ X, Y int }{ 1, 2 })  // => 1 123Go言語{ 1, 2 }

fmt.Printf

第1引数に文字列でフォーマットを指定して,第2引数以降は埋め込まれる任意のデータを並べて出力します

fmt.Printf("My name is %s\n", "Tom")  // => "My name is Tom"

fmt.Println

interface{}型の任意の数の引数をとり,さまざまなデータ型を末尾に改行付きで出力します
文字同士を「,(カンマ)」で区切って複数出力することも可能ですが,文字列型と隣接するデータは間にスペースが付きません

// どんな型でもそのまま出力
fmt.Println("アイウエオ")  // => アイウエオ(末尾に改行付き)
fmt.Println(struct{ X, Y int }{ 1, 2 })  // => { 1, 2 }(末尾に改行付き)
fmt.Println(1, 123, "Go言語", struct{ X, Y int }{ 1, 2 })  // => 1 123Go言語{ 1, 2 }(末尾に改行付き)

文字列への出力(生成)関数(Sから始まるグループ)

名前が「S」から始まる関数のグループは,文字列(String)を 「生成」 するために使います
この関数単体では標準出力などはできないため,通常のコード内では変数などにデータを格納するためなどに使います

fmt.Sprint

interface{}型の任意の数の引数をとり,文字列型を生成します
データ同士を「,(カンマ)」で区切って複数出力することも可能ですが,文字列型と隣接するデータは間にスペースが付きません

s := fmt.Sprint("アイウエオ")  // s = アイウエオ
s := fmt.Sprint(struct{ X, Y int }{ 1, 2 })  // s = { 1, 2 }
s := fmt.Sprint(1, 123, "Go言語", struct{ X, Y int }{ 1, 2 })  // s = 1 123Go言語{ 1, 2 }

fmt.Sprintf

第1引数に文字列でフォーマットを指定して,第2引数以降は埋め込まれる任意のデータを並べて文字列型を生成します

s := fmt.Sprintf("My name is %s\n", "Tom")  // s = "My name is Tom"

fmt.Sprintln

interface{}型の任意の数の引数をとり,文字列型を生成します
文字同士を「,(カンマ)」で区切って複数出力することも可能ですが,文字列型と隣接するデータは間にスペースが付きません

s := fmt.Sprintln("アイウエオ")  // s = アイウエオ(末尾に改行付き)
s := fmt.Sprintln(123)  // s = 123(末尾に改行付き)
s := fmt.Sprintln(1, 123, "Go言語", struct{ X, Y int }{ 1, 2 })  // s = 1 123Go言語{ 1, 2 }(末尾に改行付き)

ファイル(File)への出力関数(Fから始まるグループ)

名前が「F」から始まる関数のグループは,ファイル(File)への出力を行います
第1引数として渡すio.Writer型のデータに文字列などを書き出します。

io.Writerとは「何かに書き込む機能を持つものをまとめて扱うために抽象化されたもの」です

type Writer interface {
  Write(p []byte) (n int, err error)
}

例えばos.Stdout(標準出力をするためのio.Writer型パッケージ)など

fmt.Fprint

第1引数としてファイル(io.Writer型)をとり,第2引数で書き出したい文字列などを指定します
データ同士を「,(カンマ)」で区切って複数出力することも可能ですが,文字列型と隣接するデータは間にスペースが付きません

// os.Stdout (標準出力)へ書き出す
// 末尾の改行は無し
fmt.Fprint(os.Stdout, "アイウエオ")  // => アイウエオ
fmt.Fprint(os.Stdout, struct{ X, Y int }{ 1, 2 })  // => { 1, 2 }
fmt.Fprint(os.Stdout, 1, 123, "Go言語", struct{ X, Y int }{ 1, 2 })  // => 1 123Go言語{ 1, 2 }


// ファイルへ書き出す
// 末尾の改行は無し
f, err := os.Create("foo.text")  // 「foo.text」というファイルを作成し変数に格納
if err != nil {
    log.Fatal(err)  // エラーハンドリング
}
defer f.Close()

fmt.Fprint(f, "%s!\n", "hello")
// foo.textの内容
// hello!

fmt.Fprintf

第1引数にファイル(io.Writer型)を指定して,第2引数に文字列でフォーマットを指定,第3引数以降は埋め込まれる任意のデータを並べて書き出します

// os.Stdout (標準出力)へ書き出す
fmt.Fprintf(os.Stdout, "My name is %s\n", "Tom")  // => "My name is Tom"


// ファイルへ書き出す
f, err := os.Create("foo.text")  // 「foo.text」というファイルを作成し変数に格納
if err != nil {
    log.Fatal(err)  // エラーハンドリング
}
defer f.Close()

fmt.Fprintf(f, "%s!\n", "hello")
// foo.textの内容
// hello!

fmt.Fprintln

第1引数としてファイル(io.Writer型)をとり,第2引数で書き出したい文字列などを指定します
文字同士を「,(カンマ)」で区切って複数出力することも可能ですが,「文字列型と隣接するデータ」は間にスペースが付きません

// os.Stdout (標準出力)へ書き出す
// 末尾に改行付き
fmt.Fprintln(os.Stdout, "アイウエオ")  // => アイウエオ(末尾に改行付き)
fmt.Fprintln(os.Stdout, struct{ X, Y int }{ 1, 2 })  // => { 1, 2 }(末尾に改行付き)
fmt.Fprintln(os.Stdout, 1, 123, "Go言語", struct{ X, Y int }{ 1, 2 })  // => 1 123Go言語{ 1, 2 }(末尾に改行付き)


// ファイルへ書き出す
// 末尾に改行付き
f, err := os.Create("foo.text")  // 「foo.text」というファイルを作成し変数に格納
if err != nil {
    log.Fatal(err)  // エラーハンドリング
}
defer f.Close()

fmt.Fprintln(f, "hello!")
// foo.textの内容
// hello!(末尾に改行付き)

まとめ

fmtパッケージのPrint系関数は,

  • 名前が「P」から始まる関数のグループ...①
  • 名前が「S」から始まる関数のグループ...②
  • 名前が「F」から始まる関数のグループ...③

の3つのグループに分類することができ,それぞれ

  1. 標準出力への出力関数
  2. 文字列への出力(生成)関数
  3. ファイルへの出力関数

と言う役割がある

脚注
  1. 出力時の型はあくまで例です ↩︎ ↩︎

  2. 実際には任意のio.Writer型などへの出力を行う ↩︎