Go言語の学習 vol.1 / fmtパッケージ | SEEDS Creators' Blog | 株式会社シーズ

Go言語の学習 vol.1 / fmtパッケージ

この記事は Go 4 Advent Calendar 2020 の15日目の記事です。

クラウド事業部エンジニアの川勝です。
最近社内でGo言語の勉強会を不定期開催しています。
その一環で自分の学習もかねてブログでまとめていこうと思い立ちました。

vol.1 ということで Hello, world. するのに最初に通る fmt パッケージをあらためて見ていこうと思います。
書き始めてから ドキュメント を追いかければ example も実行できるしこれでいいのでは??となったのではありますが…
初心者向けに割愛してまとめようと思います。

ちなみに https://golang.org/pkg/ のexample は https://play.golang.org/ みたいにその場で実行できて編集もできる。

概要

  • Hello, world.
  • Printing
    • verbs
  • Scanning
  • まとめ

Hello, world.

Go言語で Hello, world.

package main

import "fmt"

func main() {
	fmt.Println("Hello, world.")
}

Println ?
https://golang.org/pkg/fmt/#Println

入力値(オペランド)をスペース区切りで出力(標準出力)して最後に改行がつく。可変長なのでexample のように複数の値を渡すことができる。

const name, age = "Kim", 22
fmt.Println(name, "is", age, "years old.")

Printing

Println ができたので同系統を確認。

Print https://golang.org/pkg/fmt/#Print

Println同様複数の入力値を出力します。文字列ではない場合スペースで区切られる。また最後に改行はつきません。
example をみると文字列の区切りのスペース、改行は入力値につけて渡していることがわかります。

const name, age = "Kim", 22
fmt.Print(name, " is ", age, " years old.\n")

Printf https://golang.org/pkg/fmt/#Printf

フォーマットにそって入力値を出力します。
入力値はその型に合わせて %s, %d など(verbs)に挿入されます。
verbs については後述。

const name, age = "Kim", 22
fmt.Printf("%s is %d years old.\n", name, age)

同じ出力でも Print, Printf, Println と3つの方法がありました。
違いは Example( Printers ) でもまとめられていますね。
他に Sprint, Fprint, Scan, Sscan などもありますが、それらも同様に末尾に f, ln がつくものがあります。

Sprint https://golang.org/pkg/fmt/#Sprint
Sprintf https://golang.org/pkg/fmt/#Sprintf
Sprintln https://golang.org/pkg/fmt/#Sprintln

Print系と同じですが出力ではなく結果を返します。
変数に入れてその後使いたい場合などに使用します。

const name, age = "Kim", 22
s := fmt.Sprint(name, " is ", age, " years old.\n")

Fprint https://golang.org/pkg/fmt/#Fprint
Fprintf https://golang.org/pkg/fmt/#Fprintf
Fprintln https://golang.org/pkg/fmt/#Fprintln

こちらは標準出力ではなく出力先を指定します。 出力先は io.Writter インターフェイス を満たしているいる必要があります。(インターフェイスについては取り上げません)
標準出力ではなく標準エラーに出力する場合に使うことが多いでしょうか。

const name, age = "Kim", 22
n, err := fmt.Fprint(os.Stdout, name, " is ", age, " years old.\n")

// The n and err return values from Fprint are
// those returned by the underlying io.Writer.
if err != nil {
    fmt.Fprintf(os.Stderr, "Fprint: %v\n", err)
}
fmt.Print(n, " bytes written.\n")

Errorf https://golang.org/pkg/fmt/#Errorf

これまでとちょっと毛色が違いますが、
フォーマットした文字列をerrorを見た値として返す、、
エラーメッセージに動的な値を挿入したい、というときに使うという感じでしょうか。固定のエラーメッセージであれば errors.New を使う。
example をいていただくとわかりやすいですね。

const name, id = "bueller", 17
err := fmt.Errorf("user %q (id %d) not found", name, id)
fmt.Println(err.Error())

出力系の関数は以上です。

verbs

Printf などのフォーマットで使用する verbs について。
ドキュメント にはC言語から派生してよりシンプル、、とあります。
C言語系で馴染みが無いと結構どれ使ったらいいかわからないところでもあります。
全部書くとドキュメントと同じになっちゃうのでいくつかピックアップします。
手っ取り早く試すには Example (Formats) が全部やってくれていますねw

%v

値のデフォルトフォーマット。
Print, Println 系のときはこれが使われるようですね。
デフォルトとは以下のように型に合わせてきまっているようです。
基本的に型に合わせてどれを選択するかはこれを覚えておけばいいように思いました。

bool:                    %t
int, int8 etc.:          %d
uint, uint8 etc.:        %d, %#x if printed with %#v
float32, complex64, etc: %g
string:                  %s
chan:                    %p
pointer:                 %p

さらの + # をオプションでつけることができます。
struct のfield名を出力したい場合は %+v
Go言語の構文として出力したい場合は %#v

person := struct {
	Name string
	Age  int
}{"Kim", 22}
fmt.Printf("%v\n", person)
fmt.Printf("%+v\n", person)
fmt.Printf("%#v\n", person)

上記の結果は以下になります

{Kim 22}
{Name:Kim Age:22}
struct { Name string; Age int }{Name:"Kim", Age:22}

%T

型情報を出力

%%

% を文字列として出力したい場合のエスケープ

文字列、数値のデフォルト以外verbs

普段あまり使わないもので 数値だと %b(2進数) %o 8進数で表示などもあります。%q で Go言語のシンタックスでエスケープして出力というものもあったりしました。文字列だとダブルクオーテーションがつくので便利そうかもですね。
詳細はドキュメントを。

Index

あらためてドキュメントをみて普段でも使うかな?とおもったのに引数で渡した値を index で指定できるというものがありました。

fmt.Printf("%[2]d %[1]d\n", 11, 22)

このように指定が可能です。index は 0からではなく1からというのがちょっと注意がいります。
個人的に1つの値を複数の箇所に挿入できるというのが実用箇所があるかなあと思いました。

fmt.Printf("%d %[1]d\n", 11) // `11 11`と表示

Zero Padding

0埋めはオプションの説明にのっていますがexampleはなさそうでした。
%のあとに0と桁を指定します。
‘ ‘(space) スペース埋めもあるいたいです。文字列の場合に右寄せ、とかが可能です。
オプションの – をつけるとスペースの埋める方向が逆になるようです。

	fmt.Printf("%05d\n", 5)
	fmt.Printf("% 5d\n", 5)
	fmt.Printf("%- 5d\n", 5)
00005
5    
    5
 5   

Scanning

入力値を処理します。基本的にPrint系と同じものが用意されています。
(Printより個人的に使用頻度低いので内容は駆け足)

Scan https://golang.org/pkg/fmt/#Scan

標準入力から渡された値をスペース区切りで変数にセットします。
改行はスペースに変換されるので注意。

package main

import (
	"fmt"
)

func main() {
	var s string
	var i int
	fmt.Scan(&s, &i)
	fmt.Println(s, i)
}
% echo "hello 123" | go run main.go
hello 123

Scanf https://golang.org/pkg/fmt/#Scanf
Scanln https://golang.org/pkg/fmt/#Scanln

printと同じく Scanf がフォーマットを指定します。
Scanln は改行まで処理し、最後に改行またはEOFが必要。

Sscan https://golang.org/pkg/fmt/#Sscan
Sscanf https://golang.org/pkg/fmt/#Sscanf
Sscanln https://golang.org/pkg/fmt/#Sscanln

標準入力ではなく値を引数で渡します。
例) Sscanf の example

var name string
var age int
fmt.Sscanf("Kim is 22 years old", "%s is %d years old", &name, &age)

Fscan https://golang.org/pkg/fmt/#Fscan
Fscanf https://golang.org/pkg/fmt/#Fscanf
Fscanln https://golang.org/pkg/fmt/#Fscanln

io.Reader から読み取って処理します。
例) Fscanln の example

s := `dmr 1771 1.61803398875
ken 271828 3.14159`
r := strings.NewReader(s)
var a string
var b int
var c float64
for {
	n, err := fmt.Fscanln(r, &a, &b, &c)
	if err == io.EOF {
		break
	}
	if err != nil {
		panic(err)
	}
	fmt.Printf("%d: %s, %d, %f\n", n, a, b, c)
}

まとめ

その他に Interface もいくつか用意されているのですが今回は割愛。
ScanとかPrintに渡す値が Interface を満たしていればその実装で処理できるようになったりするみたいです。(また別の機会に実験したい)

ちょっと公開する記事としては薄い内容なので迷ったのですが社内に Gopher を増やすため継続して行けたらな、、と考えています。