C# Tips | 文字列操作:複数文字列を一括置換する

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

C#で複数文字列を一括置換する方法

C#で文字列を扱っていると、ひとつの文字列だけでなく、複数の文字列をまとめて置き換えたい場面があります。たとえば、文章に含まれる古い商品名や表記ゆれをまとめて修正したり、データの形式を統一したり、不要な記号を複数種類まとめて削除したりする場合です。

このような処理をひとつずつ Replace で記述することもできますが、置換対象が増えるとコードが長くなり、修正や管理が難しくなります。そこで、置換ルールを一覧にまとめて処理する方法を覚えておくと、実務でも再利用しやすいコードを作成できます。

この記事では、C#の Replace を使った基本的な方法から、複数の置換ルールを管理する方法、正規表現を使う方法、実務で注意すべきポイントまで、初心者向けに順番に解説します。

複数文字列を一括置換するとは

複数文字列の一括置換とは、ひとつの文章や文字列に対して、複数の置換ルールを適用する処理です。

たとえば、次の文章があるとします。

旧商品は便利です。旧サービスも利用できます。旧価格を確認してください。

この文章に対して、次の3つの置換を行いたいとします。

置換前置換後
旧商品新商品
旧サービス新サービス
旧価格新価格

置換後の文章は、次のようになります。

新商品は便利です。新サービスも利用できます。新価格を確認してください。

このように、複数の置換ルールを用意しておけば、文章全体をまとめて加工できます。

C#には、複数の置換ルールを一度に指定する専用の標準 string.Replace メソッドはありません。そのため、基本的には Replace を複数回呼び出すか、置換ルールをコレクションに格納してループで処理します。

実務では、置換対象が少ない場合は個別に Replace を呼び出し、置換対象が多い場合は置換ルールを一覧として管理する方法がおすすめです。

Replaceを複数回呼び出す基本的な方法

最初に、最も簡単な方法を確認しましょう。

C#の Replace を複数回呼び出せば、ひとつの文字列に対して複数の置換を行えます。

string text = "旧商品は便利です。旧サービスも利用できます。旧価格を確認してください。";

// 1つ目の文字列を置き換えます。
text = text.Replace("旧商品", "新商品");

// 2つ目の文字列を置き換えます。
text = text.Replace("旧サービス", "新サービス");

// 3つ目の文字列を置き換えます。
text = text.Replace("旧価格", "新価格");

// 置換後の文字列を表示します。
Console.WriteLine(text);
C#

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

新商品は便利です。新サービスも利用できます。新価格を確認してください。

この方法は分かりやすく、置換対象が2~3個程度であれば十分に実用的です。

ただし、置換対象が10個、20個と増えていくと、同じようなコードを何度も書かなければなりません。置換ルールを変更するたびにコードの複数箇所を修正する必要が生じるため、管理しにくくなることがあります。

そこで、置換対象と置換後の文字列をデータとしてまとめておく方法を紹介します。

配列とループを使って一括置換する

複数の置換ルールをまとめて管理するには、配列と foreach を組み合わせる方法が便利です。

ここでは、置換前と置換後の文字列をペアにして配列に格納します。

// 置換前と置換後の文字列をペアで定義します。
(string OldValue, string NewValue)[] replacements =
{
    ("旧商品", "新商品"),
    ("旧サービス", "新サービス"),
    ("旧価格", "新価格"),
    ("旧プラン", "新プラン")
};

string text =
    "旧商品と旧サービスを確認し、旧価格と旧プランを比較してください。";

// 置換ルールを順番に適用します。
foreach (var replacement in replacements)
{
    // 現在の置換ルールを使って文字列を置き換えます。
    text = text.Replace(
        replacement.OldValue,
        replacement.NewValue);
}

// すべての置換が完了した文字列を表示します。
Console.WriteLine(text);
C#

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

新商品と新サービスを確認し、新価格と新プランを比較してください。

このコードでは、置換ルールを配列としてまとめています。foreach は、配列の要素を先頭から順番に取り出して処理するための構文です。

置換ルールを追加したい場合は、配列に新しいペアを追加するだけで済みます。置換処理そのものを何度も書く必要はありません。

置換ルールの意味を理解する

次の部分が、置換ルールを定義している箇所です。

// 置換前と置換後の文字列をペアで管理します。
(string OldValue, string NewValue)[] replacements =
{
    ("旧商品", "新商品"),
    ("旧サービス", "新サービス")
};
C#

OldValue は置換対象の文字列、NewValue は置換後の文字列を表します。

たとえば、次のペアは「旧商品」を「新商品」に置き換えるルールです。

("旧商品", "新商品")

このように、置換ルールと置換処理を分離することで、コードの役割が明確になります。

なお、この例では名前付きタプルを使用しています。名前付きタプルは、C# 7.0以降で利用できます。

Dictionaryを使って置換ルールを管理する

置換ルールを一覧として管理する方法には、Dictionary を使用する方法もあります。

Dictionary は、キーと値をペアにして管理するコレクションです。今回の場合は、置換前の文字列をキー、置換後の文字列を値として扱います。

using System;
using System.Collections.Generic;

string text =
    "旧商品は便利です。旧サービスも利用できます。旧価格を確認してください。";

// 置換前の文字列をキー、置換後の文字列を値として登録します。
var replacements = new Dictionary<string, string>
{
    ["旧商品"] = "新商品",
    ["旧サービス"] = "新サービス",
    ["旧価格"] = "新価格"
};

// 登録した置換ルールを順番に適用します。
foreach (var replacement in replacements)
{
    // キーに一致する文字列を、対応する値に置き換えます。
    text = text.Replace(
        replacement.Key,
        replacement.Value);
}

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

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

新商品は便利です。新サービスも利用できます。新価格を確認してください。

Dictionary を使用すると、置換ルールを「置換前の文字列」と「置換後の文字列」の対応表として管理できます。

ただし、Dictionary では同じキーを複数登録できません。また、複数の置換ルールを適用する順番が結果に影響する場合があるため、順番を厳密に管理したい場合は配列や List を使用するほうが分かりやすいことがあります。

実務で使える一括置換メソッドを作成する

ここまでの処理を、どこからでも再利用できるメソッドにまとめてみましょう。

このメソッドでは、次の仕様を採用します。

  • 元の文字列が null の場合は、null を返します。
  • 置換ルールは、置換前と置換後の文字列のペアで受け取ります。
  • 置換対象が空文字の場合は、例外を発生させます。
  • 置換ルールを先頭から順番に適用します。
  • 元の文字列自体を変更するのではなく、置換後の文字列を返します。
using System;
using System.Collections.Generic;

/// <summary>
/// 複数の文字列置換ルールを順番に適用します。
/// </summary>
/// <param name="text">置換対象の文字列</param>
/// <param name="replacements">置換前と置換後の文字列のペア</param>
/// <returns>置換後の文字列。元の文字列がnullならnull</returns>
static string? ReplaceMultiple(
    string? text,
    IEnumerable<(string OldValue, string NewValue)> replacements)
{
    // 元の文字列がnullなら、nullをそのまま返します。
    if (text is null)
    {
        return null;
    }

    // 置換ルールの一覧がnullなら、引数の指定ミスとして扱います。
    ArgumentNullException.ThrowIfNull(replacements);

    // 置換ルールを順番に処理します。
    foreach (var replacement in replacements)
    {
        // 置換対象がnullなら例外を発生させます。
        if (replacement.OldValue is null)
        {
            throw new ArgumentException(
                "置換対象の文字列にnullは指定できません。",
                nameof(replacements));
        }

        // 空文字を置換対象にすると意図しない処理になり得るため禁止します。
        if (replacement.OldValue.Length == 0)
        {
            throw new ArgumentException(
                "置換対象には空文字以外を指定してください。",
                nameof(replacements));
        }

        // 置換後の文字列がnullなら、空文字として扱います。
        string newValue = replacement.NewValue ?? string.Empty;

        // 現在のルールを適用し、結果を次の処理に渡します。
        text = text.Replace(replacement.OldValue, newValue);
    }

    // すべてのルールを適用した結果を返します。
    return text;
}
C#

このメソッドを使用するときは、次のように記述します。

// 複数の置換ルールを用意します。
(string OldValue, string NewValue)[] replacements =
{
    ("旧商品", "新商品"),
    ("旧サービス", "新サービス"),
    ("旧価格", "新価格")
};

string text =
    "旧商品と旧サービスを比較し、旧価格を確認してください。";

// 共通メソッドを呼び出して一括置換します。
string? result = ReplaceMultiple(text, replacements);

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

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

新商品と新サービスを比較し、新価格を確認してください。

ReplaceMultiple を用意しておけば、置換処理を利用するたびにループを書く必要がありません。置換ルールを渡すだけで同じ処理を再利用できます。

なお、このコードの ArgumentNullException.ThrowIfNull は .NET 6以降で利用できます。古い.NET環境では、if (replacements is null) のような明示的なチェックに置き換えてください。

置換する順番に注意する

複数の文字列を一括置換するときに、特に注意したいのが置換ルールの適用順序です。

置換処理は、基本的に前のルールによる変更結果を、次のルールが受け取って処理します。そのため、置換後の文字列が別の置換対象に一致すると、意図しない変更が発生することがあります。

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

string text = "A";

// 置換ルールを定義します。
(string OldValue, string NewValue)[] replacements =
{
    ("A", "B"),
    ("B", "C")
};

// 順番に置換します。
foreach (var replacement in replacements)
{
    text = text.Replace(
        replacement.OldValue,
        replacement.NewValue);
}

Console.WriteLine(text);
C#

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

C

最初の置換で「A」が「B」になり、2番目の置換で、その「B」が「C」に変わります。

もし「A」を「B」に置き換えるだけのつもりだった場合、この結果は期待と異なります。

置換ルールが重複する場合

次のようなルールにも注意が必要です。

(string OldValue, string NewValue)[] replacements =
{
    ("商品", "製品"),
    ("製品一覧", "カタログ")
};
C#

元の文字列が「商品一覧」だった場合、最初のルールで「製品一覧」になります。その後、2番目のルールが適用されるため、最終的に「カタログ」になります。

これは必ずしも間違いではありませんが、置換ルールの組み合わせによって結果が変わることを理解しておく必要があります。

対策としては、次の方法が考えられます。

  • 置換ルールを適用する順番を明確に決める。
  • 置換後の文字列が、別の置換対象に一致しないようにする。
  • 元の文字列に対して各ルールを独立して適用する必要があるか検討する。
  • 複数の置換を一度に処理する方式が必要かどうかを判断する。

特に重要なのは、「複数の Replace を順番に実行する方法」と「元の文字列に対して置換対象をまとめて探す方法」は、同じ結果になるとは限らないという点です。

置換ルールを連鎖させずに適用したい場合

前の置換結果が、次の置換ルールに影響してほしくない場合があります。

たとえば、元の文字列に存在していた「A」だけを「B」にし、元から存在する「B」は「C」にしたいとします。このとき、順番に Replace を実行すると、最初に「A」から作られた「B」も「C」に変わってしまいます。

このようなケースでは、元の文字列に対して各ルールを独立して適用する必要があります。

ただし、単純に Replace を呼び出す順番を変更するだけでは、あらゆる置換ルールの連鎖を防げるわけではありません。置換ルールを同時に適用する方式が必要な場合は、正規表現などを検討できます。

正規表現を使って複数の文字列を一度に置換する

置換対象が多く、さらに置換後の文字列が次の置換対象に影響しないようにしたい場合は、Regex.Replace を使用する方法があります。

正規表現では、複数の検索対象をひとつのパターンとして指定できます。置換対象に一致した部分を、置換後の文字列に変換する処理を一度に実行できます。

たとえば、「りんご」「みかん」「ぶどう」を、それぞれ別の文字列に置き換える場合を考えてみましょう。

using System;
using System.Text.RegularExpressions;

// 置換対象の文字列です。
string text = "りんごとみかんとぶどうを買いました。";

// 置換対象をまとめた正規表現です。
// | は「または」を意味します。
string pattern = "りんご|みかん|ぶどう";

// 一致した文字列ごとに置換後の値を返します。
string result = Regex.Replace(
    text,
    pattern,
    match =>
    {
        // 一致した文字列に応じて置換後の文字列を返します。
        return match.Value switch
        {
            "りんご" => "アップル",
            "みかん" => "オレンジ",
            "ぶどう" => "グレープ",
            _ => match.Value
        };
    });

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

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

アップルとオレンジとグレープを買いました。

この例では、Regex.Replace が元の文字列を検索し、一致した部分ごとに置換後の値を返しています。置換後に生成された「アップル」などの文字列を、同じ処理の中で改めて検索し直すわけではありません。

そのため、置換結果が別の置換対象に一致することで連鎖する問題を避けやすくなります。

正規表現の記号を理解する

この例で使用している | は、正規表現で「または」を意味します。

string pattern = "りんご|みかん|ぶどう";
C#

これは、「りんご」または「みかん」または「ぶどう」のいずれかに一致するパターンです。

ただし、置換対象に正規表現で特別な意味を持つ記号が含まれている場合は注意が必要です。たとえば、., *, +, ?, (, ) などは、正規表現の中で特別な意味を持ちます。

ユーザーが入力した文字列をそのまま正規表現のパターンに連結すると、意図しないパターンになったり、処理が複雑になったりすることがあります。動的に検索対象を組み立てる場合は、Regex.Escape を使用して、文字列を正規表現のリテラルとして扱うことを検討してください。

DictionaryとRegexを組み合わせた実務向けの例

置換ルールが多い場合は、Dictionary で置換後の値を管理し、Regex.Replace で一致した文字列を変換する方法もあります。

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

// 置換ルールを辞書で管理します。
var replacements = new Dictionary<string, string>
{
    ["旧商品"] = "新商品",
    ["旧サービス"] = "新サービス",
    ["旧価格"] = "新価格"
};

// 置換対象の文字列を用意します。
string text =
    "旧商品と旧サービスを確認し、旧価格を比較してください。";

// 辞書のキーを正規表現のリテラルとして扱い、選択パターンを作成します。
// キーに正規表現の特殊文字が含まれていても、文字列として検索できます。
string pattern = string.Join(
    "|",
    replacements.Keys.Select(Regex.Escape));

// 一致した文字列を辞書で検索し、対応する値に置き換えます。
string result = Regex.Replace(
    text,
    pattern,
    match => replacements[match.Value]);

Console.WriteLine(result);
C#

このコードを使用する場合は、先頭に次の名前空間も追加してください。

using System.Linq;
C#

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

新商品と新サービスを確認し、新価格を比較してください。

この方法では、置換ルールを辞書で管理しながら、一度の正規表現置換で処理できます。

ただし、辞書のキー同士に重なりがある場合は、どの文字列が先に一致するかによって結果が変わることがあります。たとえば「商品」と「商品一覧」の両方を置換対象にする場合は、長い文字列を先に評価するように並べ替えるなど、ルールの優先順位を設計する必要があります。

また、置換対象が非常に多い場合や、正規表現が複雑な場合は、パフォーマンスにも注意してください。小規模な置換であれば、単純な Replace の繰り返しのほうが分かりやすく、保守しやすい場合もあります。

nullや空文字を安全に扱う

実務では、置換対象の文字列が必ず存在するとは限りません。データベースや外部システムから受け取った値が null になっている場合もあります。

null に対して Replace を呼び出すと、NullReferenceException が発生します。そのため、共通メソッドでは null の扱いを明確にしておきましょう。

string? text = null;

// nullの場合は置換処理を行わず、nullを維持します。
string? result = text is null
    ? null
    : text.Replace("旧商品", "新商品");

// 表示時だけnullを別の文字列に置き換えます。
Console.WriteLine(result ?? "(値なし)");
C#

このコードでは、元の文字列が null であれば result も null になります。表示時に ?? を使用して、画面には「(値なし)」と出力しています。

一方、null や空文字をまとめて空文字に統一したい場合は、次のような方法もあります。

string? text = null;

// nullの場合は空文字として扱い、置換処理を実行します。
string result = (text ?? string.Empty)
    .Replace("旧商品", "新商品");

Console.WriteLine(result);
C#

ただし、null と空文字は業務上の意味が異なる場合があります。データが存在しない状態を示す null を、意図せず空文字に変換しないように注意してください。

置換ルールを設定ファイルやデータとして管理する

置換ルールが頻繁に変わるシステムでは、コードの中に置換対象を直接書き込むのではなく、設定ファイルやデータベースなどで管理する方法もあります。

たとえば、商品名やサービス名の表記変更が定期的に発生する場合、置換ルールをデータとして管理しておけば、プログラムの処理部分を変更せずにルールを更新しやすくなります。

ただし、外部から読み込んだ置換ルールをそのまま利用する場合は、次の点を確認してください。

  • 置換前の文字列が空文字になっていないか。
  • 置換後の文字列に意図しない値が含まれていないか。
  • 同じ置換対象が重複していないか。
  • 置換ルールの適用順序が明確になっているか。
  • 置換によってデータの意味や形式が壊れないか。

外部データを使用する場合は、読み込み時に検証することで、問題のある置換ルールが実行されることを防ぎやすくなります。

文字列置換のセキュリティ上の注意点

複数文字列の置換は、入力データの整形や表記の統一に役立ちます。しかし、文字列を置換するだけでセキュリティ上の問題を解決できるわけではありません。

たとえば、ユーザーが入力した値から特定の記号を削除してSQL文を作成する方法は、安全な対策とはいえません。SQLインジェクションを防ぐには、データベースのパラメーター化されたクエリを使用してください。

また、HTMLに文字列を出力する場合は、単純な置換で危険な文字を取り除くのではなく、出力先に適したエンコードを使用します。

// 注意:特定の記号を置換して削除するだけでは、
// SQLインジェクションなどの対策として不十分です。
// string safeText = userInput.Replace("'", "");

// SQLを実行する場合は、データベースライブラリの
// パラメーター化されたクエリ機能を使用してください。
C#

置換処理は、あくまで文字列を加工するための機能です。入力値の安全性を保証するものではありません。

特に、外部から取得した文字列をSQL、HTML、シェルコマンド、ファイルパスなどに使用する場合は、使用先に応じた安全なAPIや処理方法を選択してください。

よくある間違いと対策

Replaceの戻り値を保存しない

Replace は元の文字列を直接変更しません。戻り値を保存しなければ、置換結果を利用できません。

string text = "旧商品";

// 注意:戻り値を保存していないため、textは変わりません。
text.Replace("旧商品", "新商品");

// 正しくは、戻り値を変数に保存します。
text = text.Replace("旧商品", "新商品");

Console.WriteLine(text);
C#

置換ルールの順番を考慮しない

置換後の文字列が別の置換対象に一致すると、複数の置換が連鎖してしまう場合があります。

置換ルールを登録するときは、ルール同士の関係を確認し、適用する順番を明確にしてください。置換結果を連鎖させたくない場合は、元の文字列に対して一度に一致箇所を検索する方式を検討します。

正規表現と通常の文字列置換を混同する

string.Replace は通常の文字列を検索します。[0-9] のような正規表現パターンを指定しても、数字全体を表すパターンとしては解釈されません。

数字や特定の形式に一致する部分を置換したい場合は、Regex.Replace を使用しましょう。

空文字を置換対象に指定する

空文字を置換対象にすると、処理の意図が不明確になりやすいため、共通メソッドでは禁止する設計が有効です。

一方、置換後の文字列を空文字にすることは、指定した文字列を削除する目的で利用できます。

この2つは意味が異なるため、混同しないようにしてください。

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

一般的な業務アプリケーションで、複数の文字列を順番に置換する場合は、次のテンプレートを利用できます。

using System;
using System.Collections.Generic;

/// <summary>
/// 複数の置換ルールを順番に適用します。
/// </summary>
/// <param name="text">置換対象の文字列</param>
/// <param name="replacements">置換前と置換後の文字列の一覧</param>
/// <returns>置換後の文字列。元の文字列がnullならnull</returns>
static string? ReplaceMultiple(
    string? text,
    IEnumerable<(string OldValue, string NewValue)> replacements)
{
    // 元の文字列がnullなら、そのままnullを返します。
    if (text is null)
    {
        return null;
    }

    // 置換ルールの一覧がnullでないことを確認します。
    ArgumentNullException.ThrowIfNull(replacements);

    // 置換ルールを順番に適用します。
    foreach (var replacement in replacements)
    {
        // 置換対象がnullまたは空文字なら、例外を発生させます。
        if (string.IsNullOrEmpty(replacement.OldValue))
        {
            throw new ArgumentException(
                "置換対象には空文字以外を指定してください。",
                nameof(replacements));
        }

        // nullの置換後文字列は、削除を意味する空文字として扱います。
        string newValue = replacement.NewValue ?? string.Empty;

        // 現在の置換ルールを適用します。
        text = text.Replace(replacement.OldValue, newValue);
    }

    // すべての置換が完了した文字列を返します。
    return text;
}
C#

使用例は次のとおりです。

// 置換ルールをまとめて定義します。
(string OldValue, string NewValue)[] replacements =
{
    ("旧商品", "新商品"),
    ("旧サービス", "新サービス"),
    ("旧価格", "新価格"),
    ("不要な記号", "")
};

string text =
    "旧商品、旧サービス、旧価格を確認してください。不要な記号";

// 複数の置換ルールを適用します。
string? result = ReplaceMultiple(text, replacements);

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

このテンプレートでは、置換ルールを追加・変更するだけで、同じ処理を再利用できます。置換対象の検証もメソッド内にまとめているため、複数の箇所で同じチェックを書く必要がありません。

ただし、この方法はルールを順番に適用する方式です。置換結果を次のルールで再び置き換えたくない場合は、正規表現などを使って一度に処理する方式を検討してください。

まとめ

C#で複数文字列を一括置換するには、Replace を複数回呼び出す方法や、置換ルールを配列・Dictionary などにまとめてループで適用する方法があります。

置換対象が少ない場合は、個別に Replace を呼び出す方法が簡単です。置換対象が多い場合は、置換ルールを一覧として管理することで、追加や変更がしやすくなります。また、複数の対象を一度に検索して置換したい場合は、Regex.Replace を活用できます。

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

  • Replace の戻り値を必ず利用します。
  • 置換ルールをデータとして管理すると、保守しやすくなります。
  • 複数の置換を順番に実行すると、置換結果が連鎖する場合があります。
  • 正規表現を使用する場合は、特殊文字やパターンの意味を理解しておきます。
  • null、空文字、置換対象が存在しない場合の仕様を決めます。
  • ファイルパスやURLなどの構造化されたデータには、専用APIの利用を検討します。
  • 文字列の置換だけでセキュリティ対策を済ませず、用途に合った安全な処理を行います。

複数文字列の一括置換は、文章の修正や入力データの整形など、幅広い業務で活用できる便利な技術です。まずは Replace と配列を使った方法から始め、置換ルールの数や処理要件に応じて、共通メソッドや正規表現を使い分けていきましょう。

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