C# Tips | 文字列処理:省略文字列生成

C# C#
スポンサーリンク

はじめに 「省略文字列生成」は“長すぎる情報を、いい感じに切る”技

業務システムを作っていると、「全部は見せられないけど、雰囲気は伝えたい」という場面がよく出てきます。
ログに長いメッセージを全部出すと読めない、一覧画面でカラム幅に収まらない、ツールチップにだけ全文を出したい、などです。

そこで使うのが「省略文字列生成」、いわゆる「…で末尾を省略する」処理です。
例えば「これはとても長い説明文です」を「これはとても長い…」のように切るイメージです。

ここでは、初心者向けに、
「なぜ省略が必要になるのか」から始めて、
基本的な実装パターン、注意すべき境界条件、ユーティリティとしての設計まで、
順番にかみ砕いて説明していきます。


省略文字列の基本方針を決める

どんな仕様にしたいかを言葉にしてみる

まず、「省略文字列」と言ったときに、仕様を言葉で整理してみます。

最大長を決める。
文字列がその長さ以下なら、そのまま返す。
長さを超えているなら、「途中まで+省略記号(たとえば …)」にして返す。

例えば「最大10文字、末尾に … を付ける」という仕様なら、
「これはとても長い説明文です」は「これはとても長…」のようになります。

ここで大事なのは、「省略記号も含めて最大長に収めるかどうか」です。
多くの場合、「省略記号も含めて最大長以内」にするほうが扱いやすいです。


一番シンプルな実装パターン

末尾に “…” を付ける省略処理

まずは、半角前提でシンプルな「…ではなく ‘…’ を付ける」バージョンから見てみます。

public static class StringTruncateUtil
{
    public static string TruncateWithDots(string? value, int maxLength)
    {
        if (maxLength <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(maxLength), "maxLength は 1 以上にしてください。");
        }

        if (string.IsNullOrEmpty(value))
        {
            return string.Empty;
        }

        if (value.Length <= maxLength)
        {
            return value;
        }

        const string suffix = "...";

        if (maxLength <= suffix.Length)
        {
            return suffix.Substring(0, maxLength);
        }

        int takeLength = maxLength - suffix.Length;
        string head = value.Substring(0, takeLength);
        return head + suffix;
    }
}
C#

このメソッドがやっていることを、順番に言葉で追ってみます。

最大長が 0 以下なら、そもそもおかしいので例外にする。
null や空文字なら、空文字を返す。
元の文字列の長さが最大長以下なら、そのまま返す(省略不要)。
省略が必要な場合は、末尾に付ける文字列(ここでは “…”)を決める。
最大長が “…” より短い場合は、”..” や “.” のように、入る分だけ返す。
それ以外の場合は、「最大長 − suffix の長さ」だけ先頭から取り、その後ろに suffix を付ける。

これで、「最大長以内に収めつつ、省略されていることが分かる文字列」が作れます。

動作例でイメージを固める

Console.WriteLine(StringTruncateUtil.TruncateWithDots("これはテストです", 10));
// 長さが10以下ならそのまま(例: "これはテストです")

Console.WriteLine(StringTruncateUtil.TruncateWithDots("これはとても長い説明文です", 10));
// 例: "これはとても..." ("これはとても" + "..." で10文字以内)

Console.WriteLine(StringTruncateUtil.TruncateWithDots("短い", 2));
// 例: "短い"(そもそも2文字以下ならそのまま)

Console.WriteLine(StringTruncateUtil.TruncateWithDots("長い文字列", 2));
// 例: ".."(suffix の一部だけ入るパターン)
C#

ここまでで、「とりあえず動く省略処理」がイメージできたと思います。


省略記号を “…”(1文字)にするバージョン

日本語UIでよく使う「三点リーダー」

日本語の画面では、”…” ではなく「…」(三点リーダー)を使うことも多いです。
この場合、suffix が1文字なので、計算が少しシンプルになります。

public static class StringTruncateUtil
{
    public static string TruncateWithEllipsis(string? value, int maxLength)
    {
        if (maxLength <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(maxLength));
        }

        if (string.IsNullOrEmpty(value))
        {
            return string.Empty;
        }

        if (value.Length <= maxLength)
        {
            return value;
        }

        const string suffix = "…";

        if (maxLength == 1)
        {
            return suffix;
        }

        int takeLength = maxLength - suffix.Length;
        string head = value.Substring(0, takeLength);
        return head + suffix;
    }
}
C#

このバージョンでは、「最大長が1なら ‘…’ だけ返す」というルールにしています。
最大長が2以上なら、「先頭から maxLength − 1 文字+’…’」という形になります。


マルチバイト文字と「見た目の幅」の話

Length と「見た目の幅」は必ずしも一致しない

ここまでの実装は、value.Length を使って「文字数」で制御しています。
C# の string.Length は「UTF-16 のコード単位数」なので、
日本語1文字も英字1文字も、だいたい「1」として数えられます(サロゲートペアなどの例外はありますが、まずは置いておきます)。

ただし、コンソールや固定幅フォントの世界では、
「全角は幅2、半角は幅1」として扱われることもあります。
「見た目の幅で揃えたい」場合は、単純な Length では足りません。

このレベルに踏み込むと、「全角・半角の判定」「幅の計算」といった別のユーティリティが必要になります。
最初の一歩としては、「文字数ベースでの省略」を押さえておけば十分です。
「見た目の幅で揃えたい」という要件が出てきたら、そのときに一段階深い実装を検討する、という順番で大丈夫です。


業務ユーティリティとしての設計ポイント

null や空文字の扱いを決めておく

業務コードでは、「null が来たらどうするか」を毎回考えたくありません。
省略ユーティリティの中で、ルールを決めておくと呼び出し側が楽になります。

上の例では、「null や空文字なら空文字を返す」という方針にしています。
これなら、呼び出し側で value ?? "" といったガードを書く必要がありません。

プロジェクトによっては、「null は null のまま返す」ほうがよい場合もあります。
その場合は戻り値を string? にして、null をそのまま返す実装に変えればOKです。

省略記号や最大長を「用途ごとに固定」したメソッドを用意する

毎回 TruncateWithEllipsis(text, 20) のように書いてもよいのですが、
用途ごとに「最大長」が決まっているなら、名前付きのメソッドにしておくと読みやすくなります。

例えば、一覧画面の「説明」カラムは最大30文字、
ログのメッセージは最大100文字、という仕様なら、次のようにラップできます。

public static class AppStringShortener
{
    public static string ForListDescription(string? value)
        => StringTruncateUtil.TruncateWithEllipsis(value, 30);

    public static string ForLogMessage(string? value)
        => StringTruncateUtil.TruncateWithEllipsis(value, 100);
}
C#

呼び出し側は、「何文字にするか」ではなく「どの用途か」を意識して書けるようになります。
これは、後から仕様が変わったとき(「やっぱり40文字にしたい」など)にも効いてきます。


まとめ 「省略文字列ユーティリティ」は“全部は見せられないけど、ちゃんと伝えるためのカット編集”

省略文字列生成は、「長いから切る」というだけの話ではありません。
業務で本当に大事なのは、

最大長を決め、その中に「省略記号も含めて」収めること
短い文字列はそのまま返し、長いときだけ省略すること
null や空文字の扱いをユーティリティ側でルール化すること
用途ごとに「どの長さで切るか」を名前付きメソッドにしておくこと

といった設計の部分です。

ここまで理解できれば、「なんとなく Substring で切っている」段階から一歩進んで、
“ユーザーにも開発者にも分かりやすい、省略ルールを持った文字列ユーティリティ”を、
自分の手で設計・実装できるようになっていきます。

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