読者です 読者をやめる 読者になる 読者になる

覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。 twitter: @yyoshikaw

Go言語 - %Tや%vの書式で出力される文字列

Go言語のfmtパッケージに存在するPrintf関数は書式を指定して標準出力に書き込みを行います。
C言語のprintfに良く似ています。が、C言語には存在しない書式がいくつか加わっています。
その中の代表格として以下のような書式が存在しています。

  • %T
    • 対象データの型情報を埋め込む
  • %v
    • デフォルトフォーマットで対象データの情報を埋め込む
  • %+v
    • 構造体を出力する際に、%vの内容にフィールド名も加わる
  • %#v
    • Go言語のリテラル表現で対象データの情報を埋め込む

これらの書式は、デバッグの時などに利用すると力を発揮すると思われます。

具体的に上記の書式にいくつかデータをセットしてみて、どんな値が出力されるのかを確認してみます。
(最初のサンプル以外は、pakage, import等の記述は省略します)


整数値

package main

import (
    "fmt"
)

func main() {
    valInt := 123

    fmt.Printf("valInt[%%T] -> %T\n", valInt)
    fmt.Printf("valInt[%%v] -> %v\n", valInt)
    fmt.Printf("valInt[%%+v] -> %+v\n", valInt)
    fmt.Printf("valInt[%%#v] -> %#v\n", valInt)
}

■実行結果

valInt[%T] -> int
valInt[%v] -> 123
valInt[%+v] -> 123
valInt[%#v] -> 123

値そのものが出力されます


文字列

func main() {
    valStr := "hello golang"

    fmt.Printf("valStr[%%T] -> %T\n", valStr)
    fmt.Printf("valStr[%%v] -> %v\n", valStr)
    fmt.Printf("valStr[%%+v] -> %+v\n", valStr)
    fmt.Printf("valStr[%%#v] -> %#v\n", valStr)
}

■実行結果

valStr[%T] -> string
valStr[%v] -> hello golang
valStr[%+v] -> hello golang
valStr[%#v] -> "hello golang"

%#vの時だけ、"(ダブルクォート)で囲った値が出力されるので、スペースを含む文字列の場合などに分かりやすいかもしれません


構造体

type Person struct {
    id   int
    name string
}

func main() {
    person := Person{id: 1, name: "taro"}

    fmt.Printf("person[%%T] -> %T\n", person)
    fmt.Printf("person[%%v] -> %v\n", person)
    fmt.Printf("person[%%+v] -> %+v\n", person)
    fmt.Printf("person[%%#v] -> %#v\n", person)
}

■実行結果

person[%T] -> main.Person
person[%v] -> {1 taro}
person[%+v] -> {id:1 name:taro}
person[%#v] -> main.Person{id:1, name:"taro"}

フィールド名が出る書式(%+v, %#v)の方が分かりやすく感じます


構造体のポインタ

type Person struct {
    id   int
    name string
}

func main() {
    ptrPerson := &Person{id: 1, name: "taro"}

    fmt.Printf("ptrPerson[%%T] -> %T\n", ptrPerson)
    fmt.Printf("ptrPerson[%%v] -> %v\n", ptrPerson)
    fmt.Printf("ptrPerson[%%+v] -> %+v\n", ptrPerson)
    fmt.Printf("ptrPerson[%%#v] -> %#v\n", ptrPerson)
}

■実行結果

ptrPerson[%T]  -> *main.Person
ptrPerson[%v] -> &{1 taro}
ptrPerson[%+v] -> &{id:1 name:taro}
ptrPerson[%#v] -> &main.Person{id:1, name:"taro"}

対象がポインタであってもアドレスではなく実際の値が表示されます


配列

func main() {
    strArray := [3]string{"tokyo", "osaka", "fukuoka"}

    fmt.Printf("strArray[%%T] -> %T\n", strArray)
    fmt.Printf("strArray[%%v] -> %v\n", strArray)
    fmt.Printf("strArray[%%+v] -> %+v\n", strArray)
    fmt.Printf("strArray[%%#v] -> %#v\n", strArray)
}

■実行結果

strArray[%T] -> [3]string
strArray[%v] -> [tokyo osaka fukuoka]
strArray[%+v] -> [tokyo osaka fukuoka]
strArray[%#v] -> [3]string{"tokyo", "osaka", "fukuoka"}

配列内の全要素の内容が出力されています


スライス

func main() {
    strSlice := []string{"monkey", "cat", "dog", "man"}

    fmt.Printf("strSlice[%%T] -> %T\n", strSlice)
    fmt.Printf("strSlice[%%v] -> %v\n", strSlice)
    fmt.Printf("strSlice[%%+v] -> %+v\n", strSlice)
    fmt.Printf("strSlice[%%#v] -> %#v\n", strSlice)
}

■実行結果

strSlice[%T] -> []string
strSlice[%v] -> [monkey cat dog man]
strSlice[%+v] -> [monkey cat dog man]
strSlice[%#v] -> []string{"monkey", "cat", "dog", "man"}

配列とほぼ同様の出力内容になっています


マップ

type Person struct {
    id   int
    name string
}

func main() {
    personMap := map[int]Person{
        1: Person{id: 1, name: "taro"},
        2: Person{id: 2, name: "jiro"},
        3: Person{id: 2, name: "hanako"},
    }

    fmt.Printf("personMap[%%T] -> %T\n", personMap)
    fmt.Printf("personMap[%%v] -> %v\n", personMap)
    fmt.Printf("personMap[%%+v] -> %+v\n", personMap)
    fmt.Printf("personMap[%%#v] -> %#v\n", personMap)
}

■実行結果

personMap[%T] -> map[int]main.Person
personMap[%v] -> map[1:{1 taro} 2:{2 jiro} 3:{2 hanako}]
personMap[%+v] -> map[2:{id:2 name:jiro} 3:{id:2 name:hanako} 1:{id:1 name:taro}]
personMap[%#v] -> map[int]main.Person{3:main.Person{id:2, name:"hanako"}, 1:main.Person{id:1, name:"taro"}, 2:main.Person{id:2, name:"jiro"}}

マップ内の全要素の内容が出力されています


channel

func main() {
    ch := make(chan string, 5)
    ch <- "start"

    fmt.Printf("ch[%%T] -> %T\n", ch)
    fmt.Printf("ch[%%v] -> %v\n", ch)
    fmt.Printf("ch[%%+v] -> %+v\n", ch)
    fmt.Printf("ch[%%#v] -> %#v\n", ch)
}

■実行結果

ch[%T] -> chan string
ch[%v] -> 0x1219c3c0
ch[%+v] -> 0x1219c3c0
ch[%#v] -> (chan string)(0x1219c3c0)

channelに関してはあまり有用な情報は出力されないようです


関数

func main() {
    function := func(src int) string {
        return fmt.Sprintf("param is %d", src)
    }

    fmt.Printf("function[%%T] -> %T\n", function)
    fmt.Printf("function[%%v] -> %v\n", function)
    fmt.Printf("function[%%+v] -> %+v\n", function)
    fmt.Printf("function[%%#v] -> %#v\n", function)
}

■実行結果

function[%T] -> func(int) string
function[%v] -> 0x476e20
function[%+v] -> 0x476e20
function[%#v] -> (func(int) string)(0x476e20)

関数に関しても出力することはできますが、%vでは有用な情報は出力されません


補足

本エントリでは全てPrintf関数を用いているので結果が標準出力に書き込まれますが、Sprintf関数を使用すればフォーマットした結果を戻り値で受け取ることができます