グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



【Go】embedで作成した文字列を利用する機能のテストで改行に苦しめられた件

embedパッケージを使って作成した文字列を利用する機能のテストを書いていた際にテストが通らず苦しめられたので、備忘録も兼ねて記事にしました。

問題の概要

embedパッケージを利用してテキストファイルに書かれた文章を文字列として利用し、その文字列を text/template を使って加工してメールの文章を作る関数を作成していました。
そしてその関数のテストを書き、予想通りの文章になっているかを検証していた際に、どうしても予想の値と等しくないと判定されてしまうので、その原因を探りました。

そもそもembedとは

Goのプログラムに埋め込まれたファイルへのアクセスを提供します。以下に文字列として利用する例を載せておきます。

利用例

このような内容のテキストファイルを用意します。名前は sample_file.txt とします

春はあけぼの
夏は夜
秋は夕暮れ
冬はつとめて

このテキストファイルを利用するコードを書きます。テキストファイルとソースコードのファイルは同一ディレクトリにあるものとします。

package main

import (
	_ "embed"
	"fmt"
)

//go:embed sample_file.txt
var embedText string

func main() {
	fmt.Print(embedText)
}

上のコードを実行してみると以下が出力されます。

春はあけぼの
夏は夜
秋は夕暮れ
冬はつとめて

テキストファイルの内容が文字列として embedText 変数に代入されていることがわかります。

テストが通らなかった原因

調査

問題を切り分けるため、text/templateパッケージの機能を使っている箇所だけテストを書いてみると無事に通ったので、embedパッケージの機能を使った実装に原因があると判断し調査を進めました。 まずは以下のような内容のテキストファイル(embed_file.txt)を用意しました。

one
two
three

そしてこれをembedパッケージの機能で文字列にしたもの、同じ値になるように " で囲って作成した文字列、` で囲って作成した文字列を用意して違いがあるか検証しました。

package main

import (
	_ "embed"
	"fmt"
)

//go:embed embed_file.txt
var embedText string

func main() {
	backQuote := `one
two
three`
	doubleQuote := "one\ntwo\nthree"

	fmt.Println(embedText)
	fmt.Println(backQuote)
	fmt.Println(doubleQuote)
}

出力結果

one
two
three
one
two
three
one
two
three

出力を見る限り、三種類の方法で作成した文字列に違いは見られませんでした。次に、==演算子で比較してみました。

fmt.Println(backQuote == doubleQuote)
fmt.Println(embedText == doubleQuote)

出力結果

true
false

` で囲って作成した文字列と " で作成したものではtrueと判定されました。しかしembedで作成した文字列だとやはり同値と判定されないようです。

出力結果の内容だけでは何が違うのかわからないので、文字列をbyteスライスに変換してみることにしました。

embedBytes := []byte(embedText)
backBytes := []byte(backQuote)

fmt.Println(embedBytes)
fmt.Println(backBytes)

出力結果

[111 110 101 13 10 116 119 111 13 10 116 104 114 101 101]
[111 110 101 10 116 119 111 10 116 104 114 101 101]

どうやらembedで埋め込んだ文字列にだけ13が含まれているようです。そしてこの1310とセットで出てくるようです。

Unicodeのコードポイントの10と13は何なのかを調べました(このコードで出力された10と13は10進数)。
結果、Unicodeでは10がLF、13がCRであることがわかりました。

結論

テストが通らなかった原因は改行コードの違いでした。embedで利用したテキストファイルの改行コードがCRLFだったので、改行コードがLFのみであった他の文字列と等しくないと判定されていたようです。

どうやら改行コードはOSによって違いがあり、Windowsの改行コードはCRLFのようです(メモ帳を開くと下の方にCRLFと表記されている箇所がありました)。どうして改行コードが複数あるのかについては、色々と歴史があるようなのでここでの説明は省略させてもらいます。

本題に戻って、embed機能を使って作成した文字列をそうでない方法で作成した文字列と比較するために、以下のようなコードを書きました。

replacedEmbed := bytes.ReplaceAll(embedBytes, []byte{13}, []byte(""))
fmt.Println(string(replacedEmbed) == string(backBytes))

出力結果

true

これでGoで作成した文字列と、改行コードがCRLFのテキストファイルをembedで文字列にしたものを意図したとおりに比較することができるようになりました。

まとめ

普段パソコンやスマホで文字を打つ場合には難しいことを考えずに入力できますが、プログラムで文字を扱おうとすると思ったより複雑な仕様に翻弄されます。
また、今回もそうだったのですが、「昔似たような問題にぶつかったんだけど、どうやって解決したんだったか」といったことが稀によくあるので、問題が発生してそれを解決できたらそれで良しとせず、ドキュメントにまとめる癖をつけていこうと思いました。

この記事が同じような問題に直面している人の一助になれば幸いです。

この記事を書いた人

wanderlust
wanderlust事業開発部 web application engineer
これまで農業、士業と経験し、まったく異業種のエンジニアとしてアーティスに入社。
現在は事業開発部でバックエンドエンジニアとして仕事に従事。可読性の高いコードが書けるよう日々勉強中。趣味は一人旅。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします