C# Tips | 文字列操作:末尾を「…」で省略する

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

末尾を「…」で省略する方法

C#で長い文字列を扱っているとき、文字列の末尾を「…」に置き換えて、指定した文字数に収めたい場面があります。たとえば、一覧画面に商品名を表示するとき、ログに長いメッセージを出力するとき、データの内容を簡潔に表示するときなどです。

このような処理を実装しておくと、画面やログが読みやすくなり、限られた表示領域でも必要な情報を伝えやすくなります。

この記事では、C#の文字列操作として、文字列が指定した長さを超えた場合に末尾を「…」で省略する方法を、基本から実務で使えるテンプレートまで順番に解説します。

末尾を「…」で省略する基本的な考え方

まず、どのような処理を実現したいのかを確認しましょう。

たとえば、次のような文字列があるとします。

C#で業務に役立つ便利なユーティリティを作成する

この文字列を10文字程度で表示したい場合、文字列の途中で切り取り、末尾に「…」を追加します。

C#で業務に役...

この処理は、次の3つのステップに分けて考えると理解しやすくなります。

  1. 元の文字列が null または空文字かどうかを確認します。
  2. 文字列が指定した長さ以内なら、そのまま返します。
  3. 指定した長さを超えている場合は、途中まで切り取り、末尾に「…」を追加します。

重要なのは、切り取る文字数と「…」の文字数を合わせて、最終的な文字数を考えることです。

たとえば、最終的な文字数を10文字にしたい場合、「…」は3文字なので、元の文字列から切り取るのは7文字です。

元の文字列:C#で業務に役立つ便利なユーティリティ
切り取る部分:C#で業務に役
追加する文字:...
結果:C#で業務に役...

このように、最終的な表示文字数から省略記号の文字数を引いておけば、指定した長さに収められます。

Substringを使って文字列を省略する

C#で文字列の一部を取り出すには、Substring メソッドを使用できます。

Substring は、文字列の指定した位置から、指定した文字数分を取り出すためのメソッドです。

たとえば、次のコードを見てみましょう。

string text = "C#で業務に役立つ便利な機能";

// 先頭から5文字を取り出します。
string result = text.Substring(0, 5);

Console.WriteLine(result);
C#

実行結果は次のとおりです。

C#で業務

Substring(0, 5) の最初の引数 0 は開始位置、2番目の引数 5 は取り出す文字数を表します。

ただし、元の文字列が5文字未満なのに Substring(0, 5) を実行すると、範囲外の指定となり、ArgumentOutOfRangeException が発生します。

そのため、実務では文字列の長さを確認してから切り取ることが大切です。

基本的な省略処理を実装する

それでは、文字列が指定した長さを超えた場合だけ「…」を追加する処理を作成してみましょう。

ここでは、最終的な文字数を10文字以内に収める例を紹介します。

string text = "C#で業務に役立つ便利なユーティリティ";

// 最終的な表示文字数を指定します。
int maxLength = 10;

// 省略記号を定義します。
string suffix = "...";

string result;

// 指定した文字数以内なら、そのまま使用します。
if (text.Length <= maxLength)
{
    result = text;
}
else
{
    // 省略記号の文字数を差し引いて、切り取る文字数を求めます。
    int cutLength = maxLength - suffix.Length;

    // 先頭から必要な文字数を取り出し、省略記号を追加します。
    result = text.Substring(0, cutLength) + suffix;
}

Console.WriteLine(result);
C#

実行結果は次のようになります。

C#で業務に...

このコードでは、maxLength に最終的な表示文字数を設定しています。省略記号の ... は3文字なので、文字列本体は7文字まで切り取ります。

この方法は基本を理解するのに適していますが、実務で再利用する場合は、処理をメソッドにまとめると便利です。また、maxLength が省略記号の長さより短い場合や、null が渡された場合も考慮する必要があります。

実務で使えるメソッドにする

同じ処理を複数の場所で使うなら、専用のメソッドを作成するのがおすすめです。

ここでは、次の仕様で実装します。

  • 文字列が null または空文字なら、そのまま返します。
  • 文字列が指定した最大文字数以内なら、そのまま返します。
  • 最大文字数を超えた場合は、末尾に「…」を追加します。
  • 最終的な文字数が指定した最大文字数を超えないようにします。
  • 最大文字数が0未満の場合は、引数の指定ミスとして例外を発生させます。

以下が実務で利用できる基本的なテンプレートです。

/// <summary>
/// 文字列が指定した最大文字数を超える場合、末尾を省略記号で置き換えます。
/// </summary>
/// <param name="text">省略対象の文字列</param>
/// <param name="maxLength">省略記号を含めた最大文字数</param>
/// <param name="suffix">末尾に追加する省略記号</param>
/// <returns>省略処理後の文字列</returns>
static string TruncateWithEllipsis(
    string? text,
    int maxLength,
    string suffix = "...")
{
    // 最大文字数が負の場合は、引数の指定ミスとして扱います。
    if (maxLength < 0)
    {
        throw new ArgumentOutOfRangeException(
            nameof(maxLength),
            "最大文字数は0以上を指定してください。");
    }

    // nullや空文字は、そのまま返します。
    if (string.IsNullOrEmpty(text))
    {
        return text ?? string.Empty;
    }

    // 省略記号がnullの場合は、空文字として扱います。
    suffix ??= string.Empty;

    // すでに最大文字数以内なら、省略する必要はありません。
    if (text.Length <= maxLength)
    {
        return text;
    }

    // 最大文字数が0なら、空文字を返します。
    if (maxLength == 0)
    {
        return string.Empty;
    }

    // 省略記号自体が最大文字数以上なら、記号を優先して切り取ります。
    if (suffix.Length >= maxLength)
    {
        return suffix.Substring(0, maxLength);
    }

    // 省略記号の長さを引き、文字列本体の切り取り文字数を計算します。
    int cutLength = maxLength - suffix.Length;

    // 文字列本体と省略記号を結合して返します。
    return text.Substring(0, cutLength) + suffix;
}
C#

このメソッドをクラス内に配置し、次のように呼び出します。

string text = "C#で業務に役立つ便利なユーティリティ";

// 最大10文字で省略します。
string result = TruncateWithEllipsis(text, 10);

Console.WriteLine(result);
C#

実行結果は次のとおりです。

C#で業務に...

このメソッドでは、maxLength を「省略記号を含めた最終的な最大文字数」として扱います。そのため、戻り値の Length は、通常の文字列に対して指定した最大文字数を超えません。

メソッドの処理を順番に理解する

コードの重要な部分を確認しましょう。

まず、最大文字数が負の場合は例外を発生させています。

if (maxLength < 0)
{
    throw new ArgumentOutOfRangeException(
        nameof(maxLength),
        "最大文字数は0以上を指定してください。");
}
C#

文字数にマイナスを指定することは通常の用途では想定されません。何も考えずに処理を続けるより、呼び出し側に問題を知らせるほうが、バグを発見しやすくなります。

次に、文字列が null または空文字なら、そのまま返します。

if (string.IsNullOrEmpty(text))
{
    return text ?? string.Empty;
}
C#

C#では、null は文字列が存在しない状態を表します。null に対して Substring や Length を使用すると、実行時エラーにつながるため、先に確認しています。

続いて、元の文字列が指定した長さ以内なら、何も変更せずに返します。

if (text.Length <= maxLength)
{
    return text;
}
C#

文字列がすでに短い場合に省略記号を付ける必要はありません。この判定を先に行うことで、不要な文字列操作を避けられます。

最後に、省略記号の長さを差し引いて切り取る文字数を計算します。

int cutLength = maxLength - suffix.Length;

return text.Substring(0, cutLength) + suffix;
C#

たとえば、最大文字数が10で、省略記号が ... の場合は、次の計算になります。

最大文字数:10
省略記号の長さ:3
切り取る文字数:10 - 3 = 7

元の文字列から7文字を取り出して「…」を付けるため、最終的な長さは10文字になります。

使用例と実行結果を確認する

メソッドが正しく動作するかどうか、複数のパターンで確認してみましょう。

// 十分に長い文字列は、省略記号を付けます。
Console.WriteLine(
    TruncateWithEllipsis("C#で便利な処理を作成する", 10));

// 短い文字列は、そのまま返します。
Console.WriteLine(
    TruncateWithEllipsis("C#入門", 10));

// 空文字は、そのまま返します。
Console.WriteLine(
    TruncateWithEllipsis("", 10));

// nullは空文字として返します。
Console.WriteLine(
    TruncateWithEllipsis(null, 10));

// 最大文字数が0の場合は、空文字を返します。
Console.WriteLine(
    TruncateWithEllipsis("長い文字列", 0));

// 最大文字数が2の場合、省略記号を2文字に切り詰めます。
Console.WriteLine(
    TruncateWithEllipsis("長い文字列", 2));
C#

実行結果は次のようになります。

C#で便利な...
C#入門

長い文字列
..

空文字と null の結果は、どちらも画面上では何も表示されません。最後の例では、最大文字数が2で、省略記号が3文字あるため、.. だけが返されます。

このように、通常の文字列だけでなく、短い文字列、空文字、null、最大文字数が小さいケースも確認しておくことが大切です。

省略記号を変更する

省略記号は、必ずしも半角ピリオド3つである必要はありません。画面のデザインや用途に応じて変更できます。

たとえば、日本語の画面では「…」という三点リーダー1文字を使用する場合があります。

string text = "商品の詳しい説明文がここに入ります";

// 三点リーダーを使用して、最大10文字で省略します。
string result = TruncateWithEllipsis(
    text,
    10,
    "…");

Console.WriteLine(result);
C#

... は通常3文字として扱われますが、… は通常1文字として扱われます。そのため、同じ最大文字数を指定しても、元の文字列から取り出せる文字数は異なります。

maxLength は省略記号を含めた文字列全体の最大文字数なので、用途に合わせて省略記号を変更しても、最終的な長さの考え方は変わりません。

空白だけの文字列をどう扱うか

ここまでのメソッドでは、空文字は特別扱いしていますが、空白だけの文字列は通常の文字列として扱います。

たとえば、次の文字列です。

string text = "          "; // 半角スペースが10個
C#

この文字列は空文字ではありません。そのため、string.IsNullOrEmpty では空文字として判定されず、必要に応じて省略処理の対象になります。

空白だけの文字列も空として扱いたい場合は、string.IsNullOrWhiteSpace を使用する方法があります。

// null、空文字、空白だけの文字列を空として扱います。
if (string.IsNullOrWhiteSpace(text))
{
    return string.Empty;
}
C#

ただし、空白だけの文字列をそのまま保持する必要があるシステムでは、この判定を使うと元のデータの意味が変わってしまいます。

たとえば、入力データを忠実に保持したい場合と、画面表示用に見やすく整形したい場合では、適切な扱いが異なります。空白を空として扱うかどうかは、用途に応じて決めることが重要です。

注意点:Lengthは画面上の見た目の幅ではない

文字列を省略する処理で、特に注意したいのが string.Length の意味です。

Length は、文字列のUTF-16コード単位の数を返します。画面上で何文字分の幅を占めるか、という意味ではありません。

たとえば、英数字、漢字、絵文字などは、文字の種類によって画面上の幅や内部表現が異なります。また、一部の絵文字は複数のUTF-16コード単位で表現されます。

そのため、Substring で単純に切り取ると、絵文字などの文字を途中で分断してしまう可能性があります。

string text = "A😀BC";

// Lengthは、画面上で見える文字数と一致するとは限りません。
Console.WriteLine(text.Length);
C#

この例では、絵文字「😀」はUTF-16コード単位を2つ使用するため、文字列全体の Length は4ではなく5になります。

通常の英数字や日本語の基本的な文字を扱うだけなら、Substring による方法で十分なケースもあります。しかし、絵文字や結合文字を含む文章を扱う場合は、単純な Substring では安全に切り取れないことがあります。

文字を途中で分断しない方法

.NETでは、Unicodeテキストの書記素クラスターを扱うために、StringInfo クラスを使用できます。書記素クラスターは、一般的にユーザーが1文字として認識する単位に近いものです。

絵文字などを途中で分断せずに省略したい場合は、書記素クラスター単位で切り取る方法を検討できます。

以下は、StringInfo を使って、書記素クラスター単位で省略する例です。

using System;
using System.Globalization;

/// <summary>
/// 書記素クラスター単位で文字列を省略します。
/// </summary>
static string TruncateWithEllipsisByTextElement(
    string? text,
    int maxLength,
    string suffix = "...")
{
    // 最大文字数が負の場合は例外を発生させます。
    if (maxLength < 0)
    {
        throw new ArgumentOutOfRangeException(nameof(maxLength));
    }

    // nullや空文字は空文字として返します。
    if (string.IsNullOrEmpty(text))
    {
        return string.Empty;
    }

    // nullの省略記号は空文字として扱います。
    suffix ??= string.Empty;

    // 書記素クラスター単位で文字列を列挙します。
    TextElementEnumerator textElements =
        StringInfo.GetTextElementEnumerator(text);

    var elements = new System.Collections.Generic.List<string>();

    while (textElements.MoveNext())
    {
        // 現在の書記素クラスターをリストに追加します。
        elements.Add(textElements.GetTextElement());
    }

    // すでに最大数以内なら、元の文字列を返します。
    if (elements.Count <= maxLength)
    {
        return text;
    }

    // 最大文字数が0の場合は空文字を返します。
    if (maxLength == 0)
    {
        return string.Empty;
    }

    // 省略記号が最大文字数以上なら、その範囲に切り詰めます。
    TextElementEnumerator suffixElements =
        StringInfo.GetTextElementEnumerator(suffix);

    var suffixList = new System.Collections.Generic.List<string>();

    while (suffixElements.MoveNext())
    {
        // 省略記号も書記素クラスター単位で数えます。
        suffixList.Add(suffixElements.GetTextElement());
    }

    if (suffixList.Count >= maxLength)
    {
        return string.Concat(suffixList.GetRange(0, maxLength));
    }

    // 省略記号の分を引いて、元の文字列から取り出す数を計算します。
    int cutLength = maxLength - suffixList.Count;

    // 指定数の書記素クラスターを取り出します。
    string prefix = string.Concat(elements.GetRange(0, cutLength));

    // 元の文字列と省略記号を結合して返します。
    return prefix + suffix;
}
C#

このコードは、一般的な Length や Substring の単位ではなく、書記素クラスターを数えて切り取ります。絵文字や結合文字を含む文字列を扱う場合に、単純な切り取りよりも適しています。

ただし、書記素クラスターの扱いはUnicodeや.NETのバージョンなどにも関係します。また、文字数を数えることと、画面上の表示幅を正確に制御することは別の問題です。表示幅を厳密にそろえたい場合は、使用するフォントや画面表示の仕組みも考慮する必要があります。

なお、この例では書記素クラスターをリストに格納するため、文字列が非常に長い場合には追加のメモリを使用します。大量のデータを処理する場合は、必要な範囲だけを列挙する実装も検討してください。

実務での利用例

末尾を省略する処理は、さまざまな場面で活用できます。

一覧画面の商品名を省略する

商品一覧画面などで商品名が長い場合、表示文字数を制限することで、レイアウトを整えやすくなります。

string productName = "プログラミング初心者向けC#実践ユーティリティ集";

// 商品名を最大20文字で表示します。
string displayName = TruncateWithEllipsis(productName, 20);

Console.WriteLine(displayName);
C#

ただし、商品名を省略すると、似た名前の商品を区別しにくくなることがあります。重要な識別情報が末尾にある場合は、表示用の文字列だけを省略し、内部処理やデータベースに保存する元の値は変更しないようにしましょう。

ログに出力する文字列を短くする

ログに長いメッセージを出力すると、重要な情報を見つけにくくなることがあります。表示用のメッセージを省略すれば、ログ一覧を読みやすくできます。

string message =
    "ファイルの読み込みに失敗しました。パスやアクセス権限を確認してください。";

// 一覧表示用のメッセージを最大30文字にします。
string shortMessage = TruncateWithEllipsis(message, 30);

Console.WriteLine(shortMessage);
C#

ただし、障害調査に必要な情報を省略すると、原因を特定しにくくなります。ログ一覧では短縮版を表示し、詳細画面やログファイルでは元のメッセージを確認できるようにする設計が有効です。

また、パスワード、アクセストークン、個人情報などの機密情報は、単に省略するだけで安全になるわけではありません。ログに出力する前に、不要な機密情報を除去する設計が必要です。

入力内容のプレビューを作成する

記事本文やコメントなどを一覧表示するときにも、先頭部分だけを表示する方法が役立ちます。

string articleText =
    "この記事では、C#を使って業務で役立つ文字列操作の方法を詳しく解説します。";

// プレビュー用に最大40文字で省略します。
string preview = TruncateWithEllipsis(articleText, 40);

Console.WriteLine(preview);
C#

プレビューは内容を把握するための表示なので、元の文章を書き換える必要はありません。表示用の値と保存用の値を分けて扱うことで、データを安全に保持できます。

よくある間違いと対策

省略記号の長さを引かずに切り取る

次のようなコードでは、最終的な文字数が指定した最大文字数を超えてしまいます。

string text = "C#で業務に役立つ便利な機能";
int maxLength = 10;

// 注意:10文字を切り取った後に3文字を追加しています。
string result = text.Substring(0, maxLength) + "...";
C#

この場合、元の文字列から10文字を取り出したうえで「…」を追加するため、最終的な長さは13文字になります。

正しくは、省略記号の長さを引いてから切り取ります。

// 省略記号の長さを差し引きます。
int cutLength = maxLength - "...".Length;

// 文字列本体と省略記号を合わせて最大10文字にします。
string result = text.Substring(0, cutLength) + "...";
C#

文字列の長さを確認せずにSubstringを使う

元の文字列が短い場合に Substring で長い範囲を指定すると、例外が発生します。

string text = "C#";

// 注意:2文字の文字列から5文字を取り出そうとしています。
// string result = text.Substring(0, 5);
C#

そのため、先に text.Length と最大文字数を比較し、必要な場合にだけ切り取り処理を実行します。今回のテンプレートでは、この確認をメソッド内で行っているため、呼び出し側で毎回同じ判定を書く必要はありません。

最大文字数が省略記号より短い場合を考慮しない

最大文字数が2なのに「…」を追加すると、省略記号だけで最大文字数を超えてしまいます。

このケースでは、次のどちらの仕様にするかを決める必要があります。

  • 最大文字数を必ず守り、省略記号自体も短くする。
  • 省略記号を必ず「…」にして、最大文字数を超えることを許容する。

この記事の基本テンプレートでは、最大文字数を守ることを優先しています。そのため、最大文字数が省略記号より短い場合は、省略記号を切り詰めます。

この仕様がアプリケーションの要件に合わない場合は、メソッドの仕様を明確にしてから実装を変更してください。

すぐに使えるテンプレート

一般的な業務アプリケーションで、英数字や通常の日本語を中心に扱う場合は、次のメソッドを再利用できます。

/// <summary>
/// 指定した最大文字数を超える文字列を省略記号付きで短縮します。
/// </summary>
/// <param name="text">対象の文字列</param>
/// <param name="maxLength">省略記号を含む最大文字数</param>
/// <param name="suffix">省略記号</param>
/// <returns>短縮後の文字列</returns>
static string TruncateWithEllipsis(
    string? text,
    int maxLength,
    string suffix = "...")
{
    // 最大文字数の指定を検証します。
    if (maxLength < 0)
    {
        throw new ArgumentOutOfRangeException(nameof(maxLength));
    }

    // nullや空文字は空文字として扱います。
    if (string.IsNullOrEmpty(text))
    {
        return string.Empty;
    }

    // nullの省略記号は空文字として扱います。
    suffix ??= string.Empty;

    // 指定文字数以内なら変更しません。
    if (text.Length <= maxLength)
    {
        return text;
    }

    // 0文字指定なら空文字を返します。
    if (maxLength == 0)
    {
        return string.Empty;
    }

    // 省略記号が長すぎる場合は、最大文字数に合わせます。
    if (suffix.Length >= maxLength)
    {
        return suffix.Substring(0, maxLength);
    }

    // 省略記号を含めて最大文字数以内になるように計算します。
    int cutLength = maxLength - suffix.Length;

    // 元の文字列を切り取り、省略記号を追加します。
    return text.Substring(0, cutLength) + suffix;
}
C#

使用するときは、次のように呼び出します。

string text = "長い文字列を表示用に短くするサンプルです";

// 最大15文字で省略します。
string result = TruncateWithEllipsis(text, 15);

// 結果を表示します。
Console.WriteLine(result);
C#

このテンプレートは、元の文字列を変更せず、表示用の短い文字列を返します。最大文字数の定義、省略記号の扱い、null の処理をひとつのメソッドにまとめているため、複数の画面や処理から共通して利用できます。

ただし、絵文字などを含む文字列を厳密に扱う場合は、前述の StringInfo を使用する方法を検討してください。

12. まとめ

C#で文字列の末尾を「…」で省略するには、元の文字列の長さを確認し、指定した最大文字数を超えている場合にだけ、文字列を切り取って省略記号を追加します。

特に重要なのは、次のポイントです。

  • 文字列が短い場合は、そのまま返します。
  • 省略記号の文字数を差し引いて、切り取る文字数を計算します。
  • null や空文字を適切に処理します。
  • 最大文字数が0や省略記号より短い場合の仕様を決めます。
  • 絵文字などを扱う場合は、Length と画面上の文字数が一致しないことに注意します。
  • 表示用の文字列だけを省略し、保存用の元データは変更しないようにします。

文字列の省略処理は、一見すると簡単ですが、最大文字数の定義や特殊な文字の扱いによって結果が変わります。共通メソッドとして実装し、入力パターンごとの動作を確認しておくことで、実務でも再利用しやすく、保守しやすいコードになります。

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