倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

Go 言語の fmt パッケージにある Print 関連関数を使ってみる

Go 言語のいろいろなパッケージを使ってみるシリーズ(目次)。 今回は fmt パッケージ。

Go 言語で標準出力に文字列を表示するには、通常 fmt.Println 関数などを使いますが、fmt パッケージのパッケージドキュメントをみると、他にも "Print" が名前に入っている関数がたくさんあってちょっと混乱しそうなので、それぞれを試しに使ってみます(ただし、"%s" などのヴァーブ (verb, 動詞) には深入りしません)。 まぁ、2~3個の関数を見れば他の関数の使い方もだいたい分かるので、全部を律儀に試す必要はない気もしますが。

あと、自分は今までほとんど JVM 系の言語を使ってたので、Java で対応するメソッドなどもメモのために付記しておきます。

【この記事の内容】

サンプルコードでは import 分は省略しています。

fmt パッケージ

fmt パッケージのパッケージドキュメントはこちら。 fmt パッケージに定義されている "Print" 関数には以下のようなものがあります:

// 標準出力へ書き出す関数
func Print(a ...interface{}) (n int, err error)
func Println(a ...interface{}) (n int, err error)
func Printf(format string, a ...interface{}) (n int, err error)

// ファイル(io.Writer)へ書き出す関数
func Fprint(w io.Writer, a ...interface{}) (n int, err error)
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)

// 結果を string 値として返す関数
func Sprint(a ...interface{}) string
func Sprintln(a ...interface{}) string
func Sprintf(format string, a ...interface{}) string

// エラーを生成する関数
func Errorf(format string, a ...interface{}) error
  • interface{}Java でいう java.lang.Object 型(Scala の Any 型)、引数の「...」は可変個引数です。
  • Errorf 関数は "Print" が付いていませんが、Sprintf と似たような使い方なのでついでに使ってみます。

ではそれぞれ見ていきましょう。

標準出力へ書き出す関数

まずは標準出力へ文字列を書き出す関数です:

  • fmt.Print
  • fmt.Println
  • fmt.Printf

これら3つの関数が分かれば他の関数も簡単に把握できるので、これらの関数を少し詳しく見ていきましょう。 

fmt.Print 関数
fmt.Print 関数は引数の文字列をそのまま標準出力へ書き出します:

  // fmt.Print 関数
  fmt.Print("Hello")
  fmt.Print(", ")
  fmt.Print("world!")
  fmt.Print("\n")
  // Output:
  // Hello, world!
  //

最後のコメント内の「Output:」以下は標準出力に表示される文字列です。 go によるテストの Example 関数参照*1。 fmt.Print 関数は Java の「System.out.print()」に対応します。

引数は任意個渡すことができて、それぞれの自然な文字列表現を連結して表示します:

  // fmt.Print 関数の引数
  n := 2
  fmt.Print("Hello", ", ", "world", n, "!\n")
  // Output:
  // Hello, world2!
  //

自然な文字列表現とは、Printf 関数などで使うヴァーブで "%v" を指定したものに対応します*2。 まぁ、整数値に対してはその数値を単に文字列にしたものですが。 引数が任意個渡せるのは Java とは異なりますね。 Go では文字列と数値を "+ " 演算子で連結できないので、数値などは Print 関数に列挙して渡します(もしくはこちらの方がよく使われると思いますが、後で見る Printf 関数などを使って表示させます)。

Print 関数の返り値は、書き出したバイト数と書き出し時のエラー(正常に終了すれば nil)です:

  // fmt.Print 関数の返り値 1
  b, _ := fmt.Print("world")
  fmt.Print("\n")  // 改行
  fmt.Print(b)
  // Output:
  // world
  // 5

第1返り値は文字数ではなくバイト数です:

  // fmt.Print 関数の引数 2
  b, _ := fmt.Print("世界")
  fmt.Print("\n")
  fmt.Print(b)
  // Output:
  // 世界
  // 6

これらの返り値は fmt.Println, fmt.Printf 関数や fmt.Fprint~ 関数でも同じです。

fmt.Println 関数
fmt.Println 関数は fmt.Print 関数と使い方はほとんど同じですが、最後に改行 "\n" を書き出します:

  // Println 関数
  fmt.Print("Hello, world!")
  // Output:
  // Hello, world!
  //

まぁ fmt.Print 関数よりもよく使うかと思います。 Java の「System.out.println()」メソッドと同じですね。

引数を任意個渡せて、それぞれの引数の自然な文字列表現を表示するのは同じですが、各引数の文字列表現の間にスペースを挿入します(そして最後に改行を出力します):

  // fmt.Println 関数の引数
  n := 2
  fmt.Println("Hello", ", ", "world", n, "!\n")
  // Output:
  // Hello ,  world 2 !
  //

「2」の前後などにスペースが入っていることに注意。 それ以外は特に fmt.Print 関数と違いはありません。

返り値は fmt.Print 関数と同じですが、改行(や複数個の引数があった場合に挿入されるスペース)のバイト数もカウントされます:

  // fmt.Println 関数の返り値
  n := 2
  b, _ := fmt.Println("Hello,", "世界!", n)
  fmt.Println(b)
  // Output:
  // Hello, 世界! 2
  // 17
  //

fmt.Printf 関数
fmt.Printf 関数は、第1引数にフォーマットを指定する文字列を渡し、第2引数以降に、フォーマット文字列内にあるヴァーブに表示させる値を指定します:

  // Printf 関数
  s, n := "world", 2
  fmt.Printf("Hello, %s%d!\n", s, n)
  // Output:
  // Hello, world2!
  //

"%s" や "%d" がヴァーブです。 使用できるヴァーブに関しては fmt パッケージのパッケージドキュメントを参照。 だいたいは C や Java のものと同じじゃないかと思いますが、細かくは比較していません。 fmt.Printf 関数は Java での「System.out.printf()」メソッドに対応します。

ファイル(io.Writer)へ書き出す関数

関数名の接頭辞に「F」がつく "Print" 関数は、標準出力ではなくファイル(というか io.Writer)に文字列を書き出します:

  • fmt.Fprint
  • fmt.Fprintln
  • fmt.Fprintf

頭文字の「F」はファイルの「F」だそうです(『プログラミング言語 Go』より)。 これらはそれぞれ前節の3つの "Print" 関数と使い方は同じですが、標準出力ではなく第1引数として渡す io.Writer に文字列を書き出します。 返り値は、前節の3つの関数と同じく、書き出されたバイト数と書き出し時のエラー(正常に終了すれば nil)です。 これらの関数は Java での java.io.Writer#write() メソッドや java.io.PrintWriter#print() メソッドなどに対応します。

3つの関数について個々に細かく使い方を見ていくほどのこともないので、少しだけ凝ったサンプルコードをザッと見ていきましょう。

fmt.Fprint 関数
まずは fmt.Fprint 関数で os.Stdout つまりは標準出力に文字列を書き出すコード:

  // fmt.Fprint で os.Stdout (標準出力)へ書き出す
  fmt.Fprint(os.Stdout, "Hello, world!")
  // Output:
  // Hello, world!

まぁ fmt.Print 関数と同じことを回りくどくやってるだけな感じですが(笑)

fmt.Fprintln 関数
次は fmt.Fprintln 関数を使ってバイトバッファに文字列を書き出すコード:

  var buffer bytes.Buffer

  // fmt.Fprintln で bytes.Buffer へ書き出す
  fmt.Fprintln(&buffer, "世界")

  fmt.Println(buffer.Bytes())
  // Output:
  // [228 184 150 231 149 140 10]

結果のバイト配列の最後の「10」は改行文字 "\n" です。 これと同じことは、次節でみる fmt.Sprintln 関数で簡単にできます。

fmt.Fprintf 関数
最後は fmt.Fprintf 関数を使ってファイルに文字列を書き出すコード:

  f, err := os.OpenFile("world.txt", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0755)
    // 読み書き可 (RDWR)、ファイルが無ければ新規作成 (CREATE)、あればエラー(EXCL)
    // パーミッション (0755) rwxr-xr-x
    // でファイルを開く
  if err != nil { log.Fatal(err) }

  // fmt.Fprintf 関数でファイルへ書き出す
  fmt.Fprintf(f, "Hello, world%d!", 2)

  if err := f.Close(); err != nil { log.Fatal(err) }

  // ファイルの内容の確認
  content, err := ioutil.ReadFile("world.txt")
  if err != nil { log.Fatal(err) }
  fmt.Println(string(content))
  // Output:
  // Hello, world2!

log.Fatal 関数は、エラーメッセージを表示してプログラムを終了する関数です。 次回やる予定。 fmt.Fprintf 関数のサンプルのはずが、ファイルの準備や後始末のコードの方がかなり長くなってますが・・・

結果を string 値として返す関数

関数名の接頭辞に「S」がつく "Print" 関数は、標準出力などに文字列を書き出す代わりに、文字列を関数の返値として返します。

  • fmt.Sprint
  • fmt.Sprintln
  • fmt.Sprintf

文字列が返される以外は、前々節の3つの "Print" 関数と同じように使えます。 おそらく fmt.Sprint と fmt.Sprintln はあまり使わず、フォーマットされた文字列を返す fmt.Sprintf を使うことがほとんどだと思います。 fmt.Sprintf は Java の「java.lang.String#format()」(静的)メソッドに対応します。

fmt.Sprint 関数
fmt.Sprint 関数は引数の値の自然な文字列表現を連結した文字列を返します:

  // fmt.Sprint 関数
  s := fmt.Sprint(1, "/", 2, "=", 0.5)

  fmt.Print(s)
  // Output:
  // 1/2=0.5

fmt.Sprintln 関数
fmt.Sprintln 関数は、引数の値の自然な文字列表現を、スペースを挟んで連結し、最後に改行を追加した文字列を返します:

  // fmt.Sprintln 関数
  s := fmt.Sprintln(1, "/", 2, "=", 0.5)

  fmt.Print(s)
  // Output:
  // 1 / 2 = 0.5
  //

fmt.Sprintf 関数
fmt.Sprintf 関数は、第1引数で指定されたフォーマットに従って残りの引数をフォーマットした文字列を返します:

  // fmt.Sprintf 関数
  s := fmt.Sprintf("%d/%d = %.2f", 1, 2, 0.5)

  fmt.Print(s)
  // Output:
  // 1/2 = 0.50

エラー値を生成する関数

フォーマットされた文字列を返す fmt.Sprintf 関数と同じような関数として、フォーマットされたメッセージを持つエラーを生成する fmt.Errorf 関数があります。 自分でエラー値を生成したいときに使います:

  divExpr := func(x, y float64) (string, error) {
    if y == 0 {
      // fmt.Errorf 関数で、フォーマットされたメッセージを持つエラー値を生成する
      return "", fmt.Errorf("0での割り算: %1.f/%1.f", x, y)
    }
    return fmt.Sprintf("%1.f / %1.f = %.3f", x, y, x/y), nil
  }

  _, err := divExpr(2, 0)
  fmt.Println(err.Error())
  // Output:
  // 0での割り算: 2/0

フォーマットを行わない fmt.Error 関数や fmt.Errorln 関数のような関数はありません。 単純にエラーを作成したい場合は errors パッケージの New 関数を使います:

  divExpr := func(x, y float64) (string, error) {
    if y == 0 {
      return "", errors.New("0での割り算")
    }
    return fmt.Sprintf("%1.f / %1.f = %.3f", x, y, x/y), nil
  }

  _, err := divExpr(2, 0)
  fmt.Println(err.Error())
  // Output:
  // 0での割り算

今回は fmt パッケージに定義されているいろいろな "Print" 関数を見てきましたが、分類してみればどのような関数が定義されているのかを把握するのも難しくはないですね。 ただ、fmt パッケージ以外にも log パッケージに "Print" 関数がいくつかあるので次回はそれらを見ていくことにします。

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

*1:最後の空行が無くてもテストは通りますが。

*2:Java の Object#toString() メソッドみたいなものですが、基本型についても動作します。