【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
が含まれているようです。そしてこの13
は10
とセットで出てくるようです。
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
で文字列にしたものを意図したとおりに比較することができるようになりました。
まとめ
普段パソコンやスマホで文字を打つ場合には難しいことを考えずに入力できますが、プログラムで文字を扱おうとすると思ったより複雑な仕様に翻弄されます。
また、今回もそうだったのですが、「昔似たような問題にぶつかったんだけど、どうやって解決したんだったか」といったことが稀によくあるので、問題が発生してそれを解決できたらそれで良しとせず、ドキュメントにまとめる癖をつけていこうと思いました。
この記事が同じような問題に直面している人の一助になれば幸いです。
この記事を書いた人
-
これまで農業、士業と経験し、まったく異業種のエンジニアとしてアーティスに入社。
現在は事業開発部でバックエンドエンジニアとして仕事に従事。可読性の高いコードが書けるよう日々勉強中。趣味は一人旅。
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Twitterでフォロー
- Feedlyでフォロー