len(s)がUTF-8のバイト数を返し文字数と一致しない
Goの文字列はUTF-8のバイト列として保持されており、len(s)は文字数ではなくバイト数を返します。日本語のようなマルチバイト文字を含む文字列では、見た目の文字数より大きな値になります。
なぜエラーが出ないのか
出力: 9
(エラーなし)- Goは何も報告しません。コンパイルも実行も正常に終わるコードだからです
出力: 9- 実際の出力 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 package main 2 3 import "fmt" 4 5 func main() { 6 s := "日本語" 7 fmt.Println(len(s) ) ^ 8 }
出力: 9文字数としては3のはず
「日本語」は3文字ですが、UTF-8では1文字が3バイトなのでlen(s)は9を返します。文字数を数えたいなら[]rune(s)に変換してから数える必要があります。
直し方: len(s) を len([]rune(s)) にします。
広告
広告スロット(未設定)
パターン2
1 package main 2 3 import "fmt" 4 5 func main() { 6 s := "こんにちは" 7 fmt.Println(len(s) ) ^ 8 }
出力: 15文字数としては5のはず
5文字のひらがなも同様に1文字3バイトなので、len(s)は見た目の文字数の3倍になります。
直し方: len(s) を len([]rune(s)) にします。
パターン3
1 package main 2 3 import "fmt" 4 5 func main() { 6 s := "東京" 7 fmt.Println(len(s) ) ^ 8 }
出力: 6文字数としては2のはず
短い文字列でも同じ規則が適用され、2文字の漢字でもlen(s)は6を返します。
直し方: len(s) を len([]rune(s)) にします。
パターン4
1 package main 2 3 import "fmt" 4 5 func main() { 6 s := "プログラミング" 7 fmt.Println(len(s) ) ^ 8 }
出力: 21文字数としては7のはず
カタカナの7文字でも同様で、len(s)は21というバイト数を返し、見た目の文字数とは一致しません。
直し方: len(s) を len([]rune(s)) にします。
パターン5
1 package main 2 3 import "fmt" 4 5 func main() { 6 s := "猫" 7 fmt.Println(len(s) ) ^ 8 }
出力: 3文字数としては1のはず
1文字だけの文字列でも、len(s)はバイト数の3を返すため、見た目の文字数1とは一致しません。
直し方: len(s) を len([]rune(s)) にします。
よくある誤解
「len(s)は文字列の見た目の文字数を返すはず」という思い込みは誤りです。len(s)は常にバイト数を返すため、文字(rune)単位で数えたい場合はutf8.RuneCountInStringやrune変換したスライスの長さを使う必要があります。
まとめ
len(s)がUTF-8のバイト数を返し文字数と一致しないは中級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。