Overview
タイトルの通りです。
Go で文字列の中に改行コードが含まれていた場合 "" なら \\n のようにバックスラッシュを使ってエスケープしますが、そもそもの改行コードを含む文字列をバッククオートで出力した場合、自動で改行コードがエスケープされる、という挙動について fmt の使い方でハマるところがあったので備忘録。
改行コードをエスケープする
シンプルなエスケープは以下
func main() { s := "ハロー\\n私は太郎です。" str := fmt.Sprintf("%s", s) fmt.Println(str) } // Output: ハロー\n私は太郎です。
https://play.golang.org/p/JlSVd363_sc
バッククオートを使った場合は以下
func main() { s := `ハロー\n私は太郎です。` str := fmt.Sprintf("%s", s) fmt.Println(str) } // Output: ハロー\n私は太郎です。
https://play.golang.org/p/cUJKw6wKOdm
ダブルクオテーションで囲った文字列の場合、そのままでは改行コードがエスケープされません。
fmt package で罠にハマる
バッククオートで囲ってる改行コード付き文字列を fmt を使って%sでフォーマット出力するとエスケープされない、という挙動に出会いました。
func main() { s := "ハロー\n私は太郎です。" str := fmt.Sprintf("%s", s) fmt.Println(str) } // Output: ハロー 私は太郎です。
https://play.golang.org/p/yKBsxsoJwRD
これは""で囲われてる文字列の改行コードがそのまま評価されて出力結果が改行されます。
func main() { s := `ハロー\n私は太郎です。` str := fmt.Sprintf("%s", s) fmt.Println(str) } Output: ハロー\n私は太郎です。
https://play.golang.org/p/cUJKw6wKOdm
バッククオートで囲った時は改行コードは評価されずに文字列として出力されます。
func main() { s := `ハロー\n私は太郎です。` str := fmt.Sprintf(`"%s"`, s) fmt.Println(str) } // Output: "ハロー\n私は太郎です。"
https://play.golang.org/p/i6WgbSnT7Mt
`` で囲われた format の中において %s を使って出力のフォーマットを行う場合 "" で囲われた文字列の中に改行コードがエスケープされずにそのまま出力された文字列になります。
ここで困るのは上記のようなエスケープされていない文字列が出力されてしまう場合、これを JSON も Key などに当ててしまったら JSON の内部では "\n" が評価されしまい、意図しない改行が入ってしまいます。
このため、バッククオートで本来エスケープされるはずの改行コードをエスケープした状態にするには以下の2つの方法があります。
%q を使う -> シングルクオートで出力してくれる -> バッククオートで囲った文字列についてエスケープ済みで出力してくれる。
ref: https://play.golang.org/p/uFaY3w1d1fGhttps://pkg.go.dev/strconv#Quote を使う。
https://pkg.go.dev/strconv#example-Quote に大体使い方は書いてます。
まとめ
%q とても便利だなと思いました。
追記
String literals
"" と `` はそもそも文字列リテラルが違うので出力が異なるのは仕様。(生の文字列リテラルと解釈された文字列リテラル)
ref: https://golang.org/ref/spec#String_literals
特に以下の部分
Raw string literals are character sequences between back quotes, as in
foo
. Within the quotes, any character may appear except back quote. The value of a raw string literal is the string composed of the uninterpreted (implicitly UTF-8-encoded) characters between the quotes; in particular, backslashes have no special meaning and the string may contain newlines. Carriage return characters ('\r') inside raw string literals are discarded from the raw string value.