PHP Tips | 文字列処理:文字数・切り出し – 指定文字数で省略(…付き)

PHP PHP
スポンサーリンク

「指定文字数で省略(…付き)」がやりたいこと

やりたいことはシンプルです。

文字列が長すぎるときに、
「指定した文字数まで」で切り、
末尾に「…(三点リーダー)」を付けて短く見せる。

例えば、タイトルがこうだとします。

$title = "これはとてもとても長いお知らせタイトルです";
PHP

これを「10文字まで」にしたいなら、

これはとてもとても長いお知らせタイトルです
↓
これはとてもと…

のように、「10文字+…」という形にしたい、というユーティリティです。

ここで重要なのは、

  • 日本語(マルチバイト)でも文字化けせずに切ること
  • 途中で文字が欠けないようにすること
  • 「すでに短い文字列」はそのまま返すこと

この3つです。


なぜ「マルチバイト対応」で省略しないといけないのか

strlen と substr でやると壊れる

まず、やってはいけないパターンから。

$title = "これはとてもとても長いお知らせタイトルです";

$short = substr($title, 0, 10);  // ダメな例(バイト数で切ってしまう)

echo $short;
PHP

substr は「バイト数」で切ります。
UTF-8 の日本語は 1文字が 3バイトなので、10バイトで切ると「文字の途中」で切れてしまい、
文字化けしたり、意味不明な文字列になったりします。

mb_substr と mb_strlen を使う

日本語を含む文字列を「人間の感覚どおり」に扱いたいときは、
mb_strlenmb_substr をセットで使うのが基本です。

  • mb_strlen:マルチバイト対応の「文字数カウント」
  • mb_substr:マルチバイト対応の「部分文字列取得」

これを使えば、「何文字目まで」という単位で安全に切り出せます。


基本形:マルチバイト安全な「…付き省略」関数

まずは完成形を見てみる

実務でそのまま使える、シンプルな関数を先に出します。

/**
 * 指定文字数を超える場合に「…」を付けて省略する(UTF-8 前提)
 *
 * @param string $text   対象文字列
 * @param int    $limit  最大文字数
 * @return string        省略後の文字列
 */
function truncateWithEllipsis(string $text, int $limit): string
{
    // 現在の文字数を取得
    $length = mb_strlen($text, 'UTF-8');

    // 文字数が制限以内なら、そのまま返す
    if ($length <= $limit) {
        return $text;
    }

    // 指定文字数だけ切り出す
    $truncated = mb_substr($text, 0, $limit, 'UTF-8');

    // 末尾に「…」を付ける
    return $truncated . '…';
}
PHP

使い方はこうです。

$title = "これはとてもとても長いお知らせタイトルです";

echo truncateWithEllipsis($title, 10);
// これはとてもと…
PHP

ここから、この関数の中身をかみ砕いて説明していきます。


関数の中身を一つずつ分解して理解する

1. まず「今の文字数」を数える

$length = mb_strlen($text, 'UTF-8');
PHP

ここでやっているのは、

「この文字列は、今何文字あるのか?」

を、マルチバイト対応で数えることです。

  • strlen ではなく mb_strlen を使う
  • エンコーディング 'UTF-8' を明示する

この2つがとても重要です。

2. そもそも短いなら、そのまま返す

if ($length <= $limit) {
    return $text;
}
PHP

ここは地味ですが、実務ではかなり大事な分岐です。

  • すでに 10文字以内のタイトルを、わざわざ「…」付きにする必要はない
  • 「短いものはそのまま」「長いものだけ省略」というのが自然な挙動

なので、「制限以内なら何もしないで返す」というのが正解です。

3. 指定文字数だけ安全に切り出す

$truncated = mb_substr($text, 0, $limit, 'UTF-8');
PHP

mb_substr は、「開始位置」と「長さ」を「文字数単位」で指定できます。

  • 第1引数:元の文字列
  • 第2引数:開始位置(0 なら先頭から)
  • 第3引数:取得したい文字数
  • 第4引数:エンコーディング(ここでも 'UTF-8' を明示)

これで、「先頭から $limit 文字分だけ」を安全に切り出せます。

4. 最後に「…」を付ける

return $truncated . '…';
PHP

ここで付けているのは、全角の三点リーダー()です。
半角の ... にしたい場合は、ここを '...' に変えれば OK です。


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

例題1:記事タイトルの一覧表示

一覧画面で、タイトルが長すぎるとレイアウトが崩れるので、
「最大 20 文字まで」にしたいケースを考えます。

$title = $row['title'];

$shortTitle = truncateWithEllipsis($title, 20);

echo htmlspecialchars($shortTitle, ENT_QUOTES, 'UTF-8');
PHP

ここでのポイントは、

  • 省略処理(truncateWithEllipsis)は「生の文字列」に対して行う
  • 画面に出すときは、最後に htmlspecialchars でエスケープする

という順番です。

例題2:本文の冒頭だけを「要約」として表示

本文は長いけれど、一覧では「冒頭 50 文字だけ」を見せたい、というケース。

$body = $row['body'];  // HTML ではなくプレーンテキスト前提の例

$summary = truncateWithEllipsis($body, 50);

echo nl2br(htmlspecialchars($summary, ENT_QUOTES, 'UTF-8'));
PHP

本文がプレーンテキストなら、このまま使えます。
HTML の場合は、先にタグ除去やテキスト化が必要になりますが、
「テキストになったあと」の省略処理は同じです。


もう少しだけ踏み込んだ工夫

「…」も含めて「全体で N 文字」にしたい場合

今の関数は、

  • 本文:$limit 文字
  • そのあとに「…」

なので、全体の長さは $limit + 1 文字になります。

もし、

「“本文+…” を合わせて 10 文字以内にしたい」

という場合は、本文部分を 1 文字減らせばよいです。

function truncateWithEllipsisStrict(string $text, int $limit): string
{
    $length = mb_strlen($text, 'UTF-8');

    if ($length <= $limit) {
        return $text;
    }

    // 「…」の分を 1 文字引く
    $bodyLength = max($limit - 1, 0);

    $truncated = mb_substr($text, 0, $bodyLength, 'UTF-8');

    return $truncated . '…';
}
PHP

この場合、$limit が 10 なら、

  • 本文:9文字
  • 「…」:1文字

で、合計 10 文字になります。

末尾の空白や改行を整えてから省略する

省略前に、前後の空白や改行を整えておくと、見た目がきれいになります。

function truncateWithEllipsisTrimmed(string $text, int $limit): string
{
    // 前後の空白や改行を削る
    $text = trim($text);

    $length = mb_strlen($text, 'UTF-8');

    if ($length <= $limit) {
        return $text;
    }

    $truncated = mb_substr($text, 0, $limit, 'UTF-8');

    return $truncated . '…';
}
PHP

まとめ:今日からの「省略処理」の基準

押さえておきたいポイントだけ、最後にコンパクトに整理します。

  • 日本語を含む文字列を「指定文字数で省略」するときは、
    strlen / substr ではなく、必ず mb_strlen / mb_substr を使う。
  • 「短い文字列はそのまま返す」「長いときだけ“…付き”にする」という分岐を入れる。
  • 「…」を含めた全体の長さをどうしたいか(limit に含めるかどうか)を、最初に決めておく。

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