QuoteとUnquote #golang

October 19, 2019

Quoteを行う

Goで文字列を""で括って(Quoteして)表示したい場合、fmt.Printf関数を用いて行うことが多いでしょう。

package main

import "fmt"

func main() {
	fmt.Printf(`"%s"`, "hoge")
}

これを実行すると以下のようになります。

$ go run main.go
"hoge"

文字列中に"が含まれていた場合に、Goのソースコード上の文字列のようにエスケープする場合には%qが便利でしょう。

package main

import "fmt"

func main() {
	fmt.Printf("%[1]s %[1]q\n", "hoge")
	fmt.Printf("%[1]s %[1]q\n", `"fuga"`)
}

これを実行すると以下のようになります。

$ go run main.go
hoge "hoge"
"fuga" "\"fuga\""

なお、%[1]s%[1]q[1]は書式を適用する引数のインデックスを指します。この場合は、"hoge"`"fuga"`にあたります。インデックスを指定することで同じ引数に複数回別の書式を指定することが可能になります。

なお、単純に文字列をQuoteしたい場合はfmt.Sprintf関数を使って同様に行うこともできますが、以下のようにstrconv.Quote関数を用いた方がよいでしょう。

package main

import (
	"fmt"
	"strconv"
)

func main() {
	s1 := "hoge"
	s2 := strconv.Quote(s1)
	fmt.Println(s1, s2)
}

このコードを実行すると以下のようになります。

$ go run main.go
hoge "hoge"

Unquoteを行う

Quoteの逆を(Unquote)を行いたい場合に、strings.Trim関数を使ってstrings.Trim(str, `"`)のようにしてしまいがちですが、これでは十分にUnquoteされません。Goの文字列には`hoge`のようなものも許容されるからです。

strings.Trim関数の代わりに以下のようにstrconv.Unquote関数を用いることで簡単にUnquoteを行うことができます。

package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {
	s1 := `"hoge"`
	s2, err := strconv.Unquote(s1)
	if err != nil {
		// エラー処理
		fmt.Fprintln(os.Stderr, "Error:", err)
		os.Exit(1)
	}
	fmt.Println(s1, s2)
}

このコードを実行すると以下のように表示されます。

$ go run main.go
"hoge" hoge

hoge"fugaのようにQuoteされていない文字列が引数として渡されるため、strconv.Unquote関数は第2戻り値にエラーを返します。そのため、適切にエラー処理を行いましょう。

Unquoteと静的解析

Unquoteを行う機会はあまりなさそうですが、静的解析を行う際に非常に便利です。

静的解析で用いられるGoのプログラムを木構造で表した抽象構文木にはast.BasicLit型で表現されるノードがあります。ast.BasicLit型は数値リテラル(12310.5など)や文字列リテラル("hoge"`fuga`など)を表す構造体です。

Valueフィールドにそのリテラルの値がソースコード上の表記で文字列として保持されています。たとえば、123のような数値リテラルは"123"のような文字列で、文字列リテラルは"hoge"のような文字列で保持されます。

数値リテラルの値をint型として取得したい場合は、strconv.Atoi関数を用います。一方、文字列リテラルの値をstring型として取得したい場合は、strconv.Unquoteを用います。

この逆を行いたい場合は、数値リテラルの場合はstrconv.Itoa関数を、文字列リテラルの場合はstrconv.Quoteを用いるとよいでしょう。

このように、Goの標準ライブラリには便利な関数がたくさん用意されています。みなさんもGoDocを読み込んで便利な関数を発見してみてください。