マルチバイトな文字列を扱うときに気をつけたい話
はじめに
現在開発中のプロジェクトではORMにgormを使っている。
1日1回依存しているパッケージのバージョンを更新してPRし、CIして変更差分に問題なければMergeして可能な限りコードを新しく保てるように開発してる。
そんな矢先、gormにMergeされたPRによって日本語の文字列が入ったテストケースがこけるようになってしまった。
チームのSlackで騒いでいると「あー multi byteな文字列が考慮されていないね」と教えていただいた。
後に修正 PRされるのだが、自分でも検証してみることにした。
該当のコード
該当のコードは以下の部分
buff := gobytes.NewBuffer([]byte{})
i := 0
for pos := range str {
if str[pos] == '?' {
buff.WriteString(replacements[i])
i++
} else {
buff.WriteByte(str[pos])
}
}
str = buff.String()
一部変更して実行してみると
str1 := "hello ? world"
buff := bytes.NewBuffer([]byte{})
i := 0
for pos := range str1 {
log.Println(pos)
if str1[pos] == '?' {
log.Println(i)
i++
} else {
buff.WriteByte(str1[pos])
}
}
log.Println(buff.String())
log.Println("------------")
str2 := "こんにちは ? 世界"
buff2 := bytes.NewBuffer([]byte{})
i = 0
for pos := range str2 {
log.Println(pos)
if str2[pos] == '?' {
log.Println(i)
i++
} else {
buff2.WriteByte(str2[pos])
}
}
log.Println(buff2.String())
以下のようにマルチバイトな文字列でない場合はstringのsliceが1バイトずつだが、
マルチバイト文字列は今回の場合だと3バイトずつになっているのが分かる
そのため buff2.WriteByte(str2[pos])
で文字列が正常に処理されず buff2.String()
が正常に表示されない。
$ go run main.go
2018/02/14 00:02:35 0
2018/02/14 00:02:35 1
2018/02/14 00:02:35 2
2018/02/14 00:02:35 3
2018/02/14 00:02:35 4
2018/02/14 00:02:35 5
2018/02/14 00:02:35 6
2018/02/14 00:02:35 0
2018/02/14 00:02:35 7
2018/02/14 00:02:35 8
2018/02/14 00:02:35 9
2018/02/14 00:02:35 10
2018/02/14 00:02:35 11
2018/02/14 00:02:35 12
2018/02/14 00:02:35 hello world
2018/02/14 00:02:35 ------------
2018/02/14 00:02:35 0
2018/02/14 00:02:35 3
2018/02/14 00:02:35 6
2018/02/14 00:02:35 9
2018/02/14 00:02:35 12
2018/02/14 00:02:35 15
2018/02/14 00:02:35 16
2018/02/14 00:02:35 0
2018/02/14 00:02:35 17
2018/02/14 00:02:35 18
2018/02/14 00:02:35 21
2018/02/14 00:02:35
解決方法
修正 PRにもあるようにrune
で処理するのが良い。
str2 := "こんにちは ? 世界"
buff2 := bytes.NewBuffer([]byte{})
for i, s := range str2 {
log.Println(i, s)
if s == '?' {
log.Println("? :", i)
i++
} else {
buff2.WriteRune(s)
}
}
log.Println(buff2.String())
}
正常に表示された
$ go run main.go
2018/02/14 00:08:30 0 104
2018/02/14 00:08:30 1 101
2018/02/14 00:08:30 2 108
2018/02/14 00:08:30 3 108
2018/02/14 00:08:30 4 111
2018/02/14 00:08:30 5 32
2018/02/14 00:08:30 6 63
2018/02/14 00:08:30 ? : 6
2018/02/14 00:08:30 7 32
2018/02/14 00:08:30 8 119
2018/02/14 00:08:30 9 111
2018/02/14 00:08:30 10 114
2018/02/14 00:08:30 11 108
2018/02/14 00:08:30 12 100
2018/02/14 00:08:30 hello world
2018/02/14 00:08:30 ------------
2018/02/14 00:08:30 0 12371
2018/02/14 00:08:30 3 12435
2018/02/14 00:08:30 6 12395
2018/02/14 00:08:30 9 12385
2018/02/14 00:08:30 12 12399
2018/02/14 00:08:30 15 32
2018/02/14 00:08:30 16 63
2018/02/14 00:08:30 ? : 16
2018/02/14 00:08:30 17 32
2018/02/14 00:08:30 18 19990
2018/02/14 00:08:30 21 30028
2018/02/14 00:08:30 こんにちは 世界
まとめ
マルチバイトな文字列を扱う可能性がある場合に、stringを1文字ずつ処理するような場合は配列のpositionではなくruneで扱うようにする。