1. 導入:len()が返すのは「文字数」ではない?
Go言語で文字列の長さを取得したいとき、初心者が最初に出会うのが len() 関数です。しかし、この関数を「文字数」を数える目的で使うと、思わぬバグを生むことがあります。なぜなら、len()は文字列の「バイト数」を返す関数だからです。日本語のようなマルチバイト文字を扱う際、この違いを理解していないと、期待した動作にならないことがあります。本記事では、len()の正しい使い方と、文字数を正しくカウントする方法を解説します。
2. 基礎知識:バイト数と文字数の違い
コンピュータは文字列を「バイト(byte)」という単位でメモリに保存しています。英数字(ASCII文字)は1バイトで表現されますが、日本語(ひらがな、漢字など)はUTF-8というエンコーディング方式において、1文字が3バイト以上で表現されます。
len() は、その文字列がメモリ上で何バイト占有しているかを計算します。例えば、「a」は1バイトですが、「あ」は3バイトです。そのため、「あ」という文字列に対してlen()を使うと、結果は「1」ではなく「3」が返ってきます。
3. 実装/解決策:文字数をカウントするには?
Goで人間が直感する「文字数」を取得したい場合は、utf8.RuneCountInString() 関数を使用します。これは文字列を「ルーン(Rune)」というGo言語における文字単位(Unicodeコードポイント)に分解してカウントするため、日本語であっても正しく文字数を取得できます。
4. サンプルプログラム
以下のコードをコピーして、ご自身の環境で実行してみてください。len()とRuneCountInString()の結果の違いが明確に分かります。
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
// 日本語を含む文字列
str := "こんにちは"
// 1. len() を使用してバイト数を取得
// 「こ」「ん」「に」「ち」「は」は各3バイトなので、合計15バイトになります
byteLen := len(str)
fmt.Printf("バイト数 (len): %d\n", byteLen)
// 2. utf8.RuneCountInString を使用して文字数を取得
// こちらは正しく「5」文字とカウントされます
charLen := utf8.RuneCountInString(str)
fmt.Printf("文字数 (RuneCountInString): %d\n", charLen)
}
5. 応用・注意点:現場での使い分け
現場の開発において、これらをどう使い分けるべきでしょうか。
・len() を使うべき場面:
データの転送サイズを制限したいときや、データベースのカラム長(バイト単位)をチェックする場合など、物理的なサイズを知りたいときに使用します。
・RuneCountInString() を使うべき場面:
ユーザーが入力した文字数制限(例:パスワードは8文字以上など)を検証する際や、画面上に文字を表示する際のレイアウト計算などで使用します。
注意点:
絵文字などはさらに複雑で、複数のルーンが組み合わさって1つの文字として表示されることがあります。厳密な「見た目の文字数」を扱うのは非常に難しいため、基本的には utf8.RuneCountInString() で十分ですが、さらに高度な処理が必要な場合は外部ライブラリの検討も視野に入れましょう。まずはこの「バイト数と文字数の違い」を意識するだけで、バグのない堅牢なコードが書けるようになりますよ!

コメント