Code Fix

中級

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パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)