PHP Tips | 文字列処理:文字数・切り出し – マルチバイト安全な文字数取得

PHP PHP
スポンサーリンク

「マルチバイト安全な文字数取得」とは何か

まず前提として、PHP の文字列は「バイト列」です。
strlen() は「文字数」ではなく「バイト数」を返します。ここが最初の落とし穴です。

UTF-8 で日本語を扱うとき、1文字が 3 バイトになることが多いので、
「5文字なのに 15 と返ってくる」といった現象が起きます。
これを避けて、「人間が数える意味での“文字数”」を正しく数えるのが「マルチバイト安全な文字数取得」です。


strlen() と mb_strlen() の違いを体感する

strlen() は「バイト数」を返す

まずは失敗例から見てみましょう。

$str = "こんにちは";

echo strlen($str);  // 15(UTF-8 の場合)
PHP

「こんにちは」は 5 文字ですが、strlen() は 15 を返します。
UTF-8 では日本語 1 文字が 3 バイトなので、5 × 3 = 15 という計算になっているわけです。

英数字だけなら 1 文字 = 1 バイトなので問題になりにくいのですが、
日本語・中国語・絵文字などを扱うときには、strlen() では「人間の感覚とズレた値」になってしまいます。

mb_strlen() は「文字数」を返す

そこで登場するのが mb_strlen() です。

$str = "こんにちは";

echo mb_strlen($str, 'UTF-8');  // 5
PHP

mb_strlen() は、指定したエンコーディングに従って「文字数」を数えてくれます。
UTF-8 であれば、日本語 1 文字も英字 1 文字も「1」としてカウントされます。

ここで重要なのは、

  • strlen() → バイト数
  • mb_strlen() → 文字数(エンコーディングを考慮)

という役割の違いを、はっきり頭の中で分けておくことです。


mb_strlen() の基本的な使い方

シグネチャとパラメータ

mb_strlen() の定義はこうなっています。

int mb_strlen(string $string, ?string $encoding = null)
PHP

第1引数が対象の文字列、第2引数がエンコーディングです。

$str = "こんにちは";

$len = mb_strlen($str, 'UTF-8');

echo $len;  // 5
PHP

第2引数のエンコーディングを省略すると、「内部エンコーディング」が使われますが、
実務では基本的に 'UTF-8' を明示することを強くおすすめします。

理由はシンプルで、「プロジェクト全体が UTF-8 前提で動いている」ことがほとんどだからです。
明示しておけば、「設定の違いで挙動が変わる」という事故を防げます。

mbstring 拡張が有効であること

mb_strlen() を使うには、PHP の mbstring 拡張が有効になっている必要があります。

最近の環境では最初から有効なことが多いですが、もし「関数が未定義」と怒られたら、
PHP の設定(拡張モジュール)を確認する必要があります。


実務での具体的なシチュエーション例

例題1:入力文字数の制限(バリデーション)

よくあるのが、「プロフィールの自己紹介は 200 文字まで」といった制限です。

$bio = $_POST['bio'] ?? '';

$len = mb_strlen($bio, 'UTF-8');

if ($len > 200) {
    $error = "自己紹介は200文字以内で入力してください。(現在:{$len}文字)";
}
PHP

ここで strlen() を使ってしまうと、日本語が混ざった瞬間に「バイト数」で判定されてしまい、
ユーザーの感覚とズレたエラーメッセージになります。

「ユーザーが見ている“文字数”」で制限したいときは、必ず mb_strlen() を使うべきです。

例題2:一覧用に「タイトルの長さ」をチェックする

記事タイトルなどで、「長すぎるタイトルはレイアウトを崩すから、30文字までにしたい」というケース。

$title = $_POST['title'] ?? '';

if (mb_strlen($title, 'UTF-8') > 30) {
    $error = "タイトルは30文字以内で入力してください。";
}
PHP

ここでも、英語だけなら strlen() でもたまたま動きますが、
日本語が混ざった瞬間に破綻します。


もう一歩踏み込む:エンコーディングと「何を 1 文字とみなすか」

エンコーディングを間違えると結果も狂う

mb_strlen() は「指定したエンコーディングに従って」文字数を数えます。

例えば、実際の文字列が UTF-8 なのに、誤って 'SJIS' を指定すると、
正しく数えられないどころか、場合によってはエラーや文字化けの原因になります。

だからこそ、

  • アプリ全体の文字コードは UTF-8 に統一する
  • mb_strlen() には 'UTF-8' を明示する

というセットを「お約束」にしてしまうのが、実務では一番安全です。

「見た目の 1 文字」と「コード上の 1 文字」がズレるケース

さらに細かい話をすると、絵文字や結合文字(アクセント付き文字など)では、
「見た目 1 文字」が複数のコードポイントで構成されることがあります。

このレベルまで厳密に「見た目の 1 文字」を数えたい場合は、
mb_strlen() ではなく grapheme_strlen() などを検討することになります。

ただし、一般的な日本語 Web システムでは、まずは mb_strlen() を使うだけで十分なことがほとんどです。
「絵文字を含めた完全な“見た目の 1 文字”カウント」は、もう一段高度な世界だと覚えておけば OK です。


実務ユーティリティとしてまとめる

共通関数としてラップしておく

毎回 mb_strlen($str, 'UTF-8') と書くのが面倒なら、
プロジェクト共通のユーティリティ関数としてラップしておくと便利です。

/**
 * マルチバイト安全な文字数取得(UTF-8 前提)
 */
function mb_length(string $str): int
{
    return mb_strlen($str, 'UTF-8');
}
PHP

使い方はこうなります。

$len = mb_length("こんにちは");  // 5

if ($len > 200) {
    // バリデーションエラー
}
PHP

「このプロジェクトでは文字数は全部 mb_length() で数える」というルールにしておけば、
strlen() をうっかり混ぜてしまう事故を防げます。

strlen() を使う場面は「バイト数が欲しいとき」だけに限定する

逆に、strlen() を使うのは、

  • バイナリデータの長さを知りたいとき
  • プロトコル上「バイト数」で長さを送る必要があるとき

など、「バイト数が欲しい」と明確に分かっている場面だけに絞るとよいです。


まとめ:今日からの「文字数カウント」の基準

ここまでをギュッとまとめると、実務での基準はこうなります。

  • 日本語を含む「人間が読む文字列」の文字数を数えたい
    mb_strlen($str, 'UTF-8') を使う
  • その呼び出しを簡単にするために、mb_length() のようなラッパー関数を用意してもよい
  • strlen() は「バイト数が欲しいときだけ」に限定する

タイトルとURLをコピーしました