LoginSignup
26
13

More than 1 year has passed since last update.

Go text/template で文字列作成

Last updated at Posted at 2020-03-07

Go text/template で文字列作成

みなさん、こんにちは!
Go書いてますか?

今回は、text/template を使って ひな形 から 文字列 を生成する方法をご紹介します。

text/template

公式が作成している 標準package なので すぐに使えます。

似たようなpackageで html/template というのがありますが、
これはコードインジェクションに対して安全なHTML出力を生成するものなので
そういった目的がないときは、 text/template を使用しましょう。

ちなみにどちらも使い方は、同じです。

使ってみる

文字列のひな形 と データ を入れ込むことによって、目的の文字列を作成します。

ソースコード
package main

import (
    "log"
    "os"
    "text/template"
)

func main() {
    // ひな形
    tmpl := "Hello {{.}}!\n"

    // template.New(<テンプレート名>).Parse(<文字列>)
    t, err := template.New("sample").Parse(tmpl)
    if err != nil {
        log.Fatal(err)
    }
    // Execute(io.Writer(出力先), <データ>)
    if err = t.Execute(os.Stdout, "World"); err != nil {
        log.Fatal(err)
    }
}
出力結果
Hello World!

上記ソースのコメントのように、それぞれ指定してデータを入れるだけでできます。

構造体を使用するとき

先ほどは 1つの値のみ を データ として入れていましたが
雛形の中に 複数の違う値 を入れたいときがあると思います。

1つの方法として、構造体(struct) を使用する方法を紹介します。

ソースコード
package main

import (
    "log"
    "os"
    "text/template"
)

// 構造体(すべて公開)
type Language struct {
    Name string
    URL  string
}

func main() {
    // 構造体に値を設定
    data := Language{
        Name: `Go`,
        URL:  `https://golang.org/`,
    }

    // ひな形
    tmpl := "今回紹介する言語は、{{.Name}}です!\n詳しくは、{{.URL}} を見てください。\n"

    // New(<テンプレート名>).Parse(<文字列>)
    t, err := template.New("sample").Parse(tmpl)
    if err != nil {
        log.Fatal(err)
    }
    // Execute(io.Writer(出力先), <データ>)
    if err = t.Execute(os.Stdout, data); err != nil {
        log.Fatal(err)
    }
}
ひな形(tmpl)
今回紹介する言語は{{.Name}}です
詳しくは{{.URL}} を見てください
  • {{.メンバ名}}
    • 構造体のメンバを指定すると値が挿入されます
出力結果
今回紹介する言語は、Goです!
詳しくは、https://golang.org/ を見てください。

雛形ファイルとの連携

雛形ファイル と連携する場合は、テキストファイル を読みこむ必要がありますが
ParseFiles() 使用すると簡単に読み込むことができます。

ソースコード
package main

import (
    "log"
    "os"
    "text/template"
)

func main() {
    // mapの使用例
    data := map[string]string{
        "Name":     "hiro",
        "Language": "Go",
    }

    // New(<ファイル名>).ParseFiles(<ひな形ファイル>)
    t, err := template.New("message.tmpl").ParseFiles("template/message.tmpl")
    if err != nil {
        log.Fatal(err)
    }

    // Execute(io.Writer(出力先), <データ>)
    if err = t.Execute(os.Stdout, data); err != nil {
        log.Fatal(err)
    }
}

入力データをmapで扱う場合も同じように指定すれば出力されます

template\message.tmpl
My name is {{.Name}}.
Programming in {{.Language}} language.
  • {{.キー名}}
    • map[string]stringのキー名を指定すると値が挿入されます
出力結果
My name is hiro.
Programming in Go language.

また1行で処理することもできます。

ソースコード
package main

import (
    "log"
    "os"
    "text/template"
)

func main() {
    // mapも使用できます
    data := map[string]string{
        `Name`:     `hiro`,
        `Language`: `Go`,
    }

    // Must(テンプレート) ・・・ テンプレートにエラーがあったらpanicする
    t := template.Must(template.ParseFiles(`template/message.tmpl`))

    // Execute(io.Writer(出力先), データ)
    if err := t.Execute(os.Stdout, data); err != nil {
        log.Fatal(err)
    }
}

雛形ファイル内での制御構文

雛形ファイル内で if を使ったり、range で スライス・配列 を回すことができます。

ソースコード
package main

import (
    "log"
    "os"
    "text/template"
)

type Member struct {
    Name    string
    Comment string
}

func main() {
    // 構造体のデータ
    list := []Member{
        {
            Name:    "Ken",
            Comment: "hello! :)",
        },
        {
            Name:    "Nancy",
            Comment: "",
        },
    }

    // 1行で終わらせるとき
    // Must(テンプレート) ・・・ テンプレートにエラーがあったらpanicする
    t := template.Must(template.ParseFiles(`template/message.tmpl`))

    // Execute(io.Writer(出力先), データ)
    if err := t.Execute(os.Stdout, list); err != nil {
        log.Fatal(err)
    }
}
template/message.tmpl
{{ range $i, $member := . -}}
名前    : {{ $member.Name }}
{{ if ne $member.Comment "" -}}
コメント: {{ $member.Comment }}
{{ else -}}
コメントはありません
{{ end }}
{{- end }}
  • range
    • goのrange関数とほぼ同じ使い方(range 配列番号, 要素 := 配列)
    • 右辺の.は、下記のソースで入れたlistがそのまま入ってきます
  • if
    • 条件式を書く
  • ne (not equal : !=)
    • ne A Bという風に指定する
    • 同じとする場合は、eq A B (equal : ==)
  • end
    • 制御構文の末尾を囲うように配置する
  • {{-
    • 左側にある改行・スペースを削除
  • -}}
    • 右側にある改行・スペースを削除
出力結果
名前    : Ken
コメント: hello! :)
名前    : Nancy
コメントはありません

まだその他にもたくさん使えるものがあります。

自作関数を使用する

自作関数も追加できるので、いろいろ幅が広がりそうです。

ソースコード
package main

import (
    "log"
    "os"
    "text/template"
)

func main() {
    // 追加で使いたい関数を定義する
    funcMap := template.FuncMap{
        "getHogeString": getHogeString,                       // 追加する関数(※1)
        "sum":           func(x, y int) int { return x + y }, // 無名関数でもOK
    }

    // New(ファイル名).ParseFiles(雛形ファイル)
    t, err := template.New("message.tmpl").Funcs(funcMap).ParseFiles("template/message.tmpl")
    if err != nil {
        log.Fatal(err)
    }

    // Execute(io.Writer(出力先), データ)
    if err := t.Execute(os.Stdout, nil); err != nil {
        log.Fatal(err)
    }
}

// ※1: 追加する関数
func getHogeString() string {
    return "Hoge!"
}

template/message.tmpl
Say {{ getHogeString }}

1 + 2 = {{ sum 1 2 }}
出力結果
Say Hoge!

1 + 2 = 3

まとめ

雛形を使った文字列作成をご紹介しました。

Executeの出力先がio.Writerなので、直接ファイルに書くこともできるので便利です。
是非使ってみてください。

26
13
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
26
13