【PHP】mb_strlenやmb_strwidthは使えない!?

PHPの覚書です。

strlenとmb_strlenの違い

  • strlen: バイト数を数えます。
  • mb_strlen: 文字数を数えます。
  • mb_strlen(“こんにちは”) は 5 を返します。
  • mb_strlen(“Hello”) も 5 を返します。
$text = "あいうABC123";
echo strlen($text);    // 出力: 15 (3*3 + 3 + 3 = 15バイト)
echo mb_strlen($text); // 出力: 9 (文字数を正確に数える)

UTF-8での文字のバイト数

  • 英語(ASCII文字): 1バイト
  • 日本語(ひらがな、カタカナ、漢字): 3バイト
  • 一部の特殊文字や絵文字: 4バイト

mb_strwidthについて

  • 文字の表示幅を考慮します。
  • 半角: 1として数える
  • 全角: 2として数える
$text = "あいうABC123";
echo mb_strwidth($text); // 出力: 12 (3*2 + 3 + 3 = 12)
スポンサーリンク

mb_strwidthは使えない?

パーフェクトじゃないけど、普通に使っているようですね。

  • mb_strwidthは全角文字を2、半角文字を1としてカウントしますが、これは必ずしも実際の表示幅と一致しません。
  • 特に問題になるのは以下のような文字です:
    • 結合文字(例:アクセント記号)
    • 絵文字
    • サロゲートペア(Unicode の拡張領域の文字)

mb_strwidthが間違える主なケース:

  • 絵文字: 多くの絵文字は2とカウントされますが、実際の表示幅はより広いことがあります。
  • 結合文字: アクセント記号などが別にカウントされ、実際の表示より広く見積もられることがあります。
  • 一部の特殊な Unicode 文字: 表示幅が正確に判断できない場合があります。

mb_strwidthは多くの一般的なケースで十分に機能しますが、完璧ではありません。以下のように考えます:

a. 実用性:

  • 通常の日本語テキストや英数字を扱う場合、mb_strwidthは十分に実用的です。
  • 多くのWebアプリケーションやCMSでの使用には適しています。

b. 限界:

  • 絵文字を多用するSNSアプリケーションや、多言語対応が必要な国際的なアプリケーションでは注意が必要です。
  • 正確な表示幅が極めて重要な場合(例:厳密なレイアウトが要求される印刷物)には不適切かもしれません。

c. 代替案:

  • より正確な幅計算が必要な場合、IntlCharクラスの使用や、実際のレンダリング結果を測定する方法を検討する必要があります。
  • ただし、これらの方法はより複雑で、実装コストが高くなる可能性があります。

推奨アプローチ:

  • まずはmb_strwidthを使用し、問題が生じた特定のケースに対してのみ、より複雑な解決策を適用することを推奨します。
  • アプリケーションの要件と対象ユーザーを考慮し、必要に応じて精度と実装の簡便さのバランスを取ることが重要です。
スポンサーリンク

mb_strwidthの文字化けで使えない?

この文字化け問題でハマったことがあります。コードが複雑だったため原因究明に時間がかかりました。

mb_substrmb_strwidthから、mb_strimwidthmb_strlenへの置き換えることにより回避しました。

mb_substr($string, $start, $length, $encoding)

  • マルチバイト文字列から、指定した位置($start)から指定した長さ($length)の部分文字列を取得します。
  • 文字数を基準に部分文字列を取得します。

mb_strwidth($string, $encoding)

  • 文字列の表示幅を計算します。半角文字を1、全角文字を2としてカウントします。
  • 表示上の幅を基準に計算します。

mb_strimwidth($string, $start, $width, $trimmarker, $encoding)

  • 指定した表示幅($width)に収まるように文字列を切り取ります。
  • 文字の途中で切り取らず、安全に表示幅を制限できます。
  • $trimmarker を指定すると、切り取った後にその文字列を追加できます(今回は空文字列を指定)。

mb_strlen($string, $encoding)

  • マルチバイト文字列の文字数を取得します。
  • 文字数を基準に長さを計算します。

簡単なサンプルです。

// ======= mb_substr =======
// 文字列から指定した位置から指定した長さの部分文字列を取得
$text = "こんにちは世界!";
echo mb_substr($text, 0, 3); // => "こんに"
echo mb_substr($text, 3, 2); // => "ちは"
echo mb_substr($text, -2);   // => "界!" (負の位置は末尾からカウント)

// ======= mb_strwidth =======
// 文字列の表示幅を取得(半角1、全角2としてカウント)
$text = "Hello世界!";
echo mb_strwidth($text); // => 8
// H(1) + e(1) + l(1) + l(1) + o(1) + 世(2) + 界(2) + !(2) = 11

// ======= mb_strimwidth =======
// 指定した表示幅に収まるように文字列を切り取り、必要に応じて末尾文字列を追加
$text = "とても長い文章です。最後まで読めません。";
echo mb_strimwidth($text, 0, 10, "..."); // => "とても長い..."
echo mb_strimwidth($text, 0, 15, "→続く"); // => "とても長い文章→続く"

mb_strimwidth文字化けする場合は第五引数で文字コードを設定して回避するとよさそうです。

エンコードを指定したサンプルです。

<?php
// UTF-8文字列のサンプル
$text = "こんにちは、World!";

// mb_strwidth: 文字列の表示幅を取得(半角1、全角2としてカウント)
$width = mb_strwidth($text, "UTF-8");
echo "mb_strwidth の結果: " . $width . "\n";  // 結果: 14 (全角5文字×2 + 半角6文字×1)

// mb_strimwidth: 指定した表示幅に文字列を切り詰め
$trimmed = mb_strimwidth($text, 0, 10, "...", "UTF-8");
echo "mb_strimwidth の結果: " . $trimmed . "\n";  // 結果: "こんにち..."

// 実践的な使用例:表示幅が異なる場合の比較
$strings = ["Hello", "こんにちは", "Hello!"];
foreach ($strings as $str) {
    echo sprintf(
        "文字列: %s, 長さ: %d, 表示幅: %d\n",
        $str,
        mb_strlen($str, "UTF-8"),
        mb_strwidth($str, "UTF-8")
    );
}

ご参考になれば幸いです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする