閉じる

【C#】Regex.Replaceで正規表現置換!基本から応用まで徹底解説

C#において文字列の操作はプログラム開発の核となる要素の一つです。

単純な固定文字列の置換であれば string.Replace メソッドで事足りますが、特定のパターンに基づいた柔軟な置換が必要な場面では、正規表現(Regular Expressions) を活用した Regex.Replace メソッドが不可欠となります。

Regex.Replace は、テキスト内から指定したパターンに一致する箇所を探し出し、別の文字列へ効率的に置き換えるための強力なツールです。

本記事では、基本的な使い方から、後方参照を用いた高度な置換、さらには最新の .NET におけるパフォーマンス最適化の手法まで、プロの視点で徹底的に解説します。

Regex.Replace の基本概念と構文

Regex.Replace メソッドを使用するには、System.Text.RegularExpressions 名前空間をインポートする必要があります。

このメソッドは、大きく分けて「静的メソッド」と「インスタンスメソッド」の 2 種類が存在します。

まずは、最もシンプルな静的メソッドの基本的な構文を見てみましょう。

C#
using System.Text.RegularExpressions;

string input = "2024/01/01";
string pattern = @"/";
string replacement = "-";

// 静的メソッドによる置換
string result = Regex.Replace(input, pattern, replacement);
// 結果: 2024-01-01

この例では、日付の区切り文字である /- に置換しています。

静的メソッドは、正規表現オブジェクトを明示的にインスタンス化することなく、1 行で簡潔に記述できる点がメリットです。

主要なオーバーロード

Regex.Replace には多くのオーバーロードが用意されており、用途に応じて使い分けることができます。

代表的なものを以下の表にまとめました。

メソッド形式第1引数第2引数第3引数概要
静的string inputstring patternstring replacement入力文字列に対してパターン一致箇所を置換する。
静的string inputstring patternMatchEvaluatorデリゲート(ラムダ式)を用いて動的に置換内容を決定する。
インスタンスstring inputstring replacementインスタンス化されたパターンに基づき置換する。
静的string inputstring patternstring replacement, RegexOptionsオプション(大文字小文字無視など)を指定して置換する。

静的メソッドとインスタンスメソッドの使い分け

C# の Regex クラスには、先ほど紹介した「静的メソッド」と、クラスをインスタンス化して使用する「インスタンスメソッド」があります。

これらは動作の結果こそ同じですが、パフォーマンス面で大きな違い があります。

静的メソッドが適しているケース

静的メソッド(Regex.Replace(input, pattern, replacement))は、特定のパターンによる置換を 一度しか行わない場合 や、アプリケーション全体で置換の頻度が極めて低い場合に適しています。

記述がシンプルになるため、コードの可読性を保ちやすいという特徴があります。

インスタンスメソッドが適しているケース

同じ正規表現パターンをループ内で何度も再利用する場合や、大量の文字列を処理する場合は、インスタンスメソッドを使用するべきです。

C#
// 正規表現オブジェクトを事前に生成
var regex = new Regex(@"\d+");
string input = "Item1, Item2, Item10";

// 同じインスタンスを使い回して置換
string result = regex.Replace(input, "NUM");

インスタンス化を行うことで、正規表現パターンの解析(パース)が一度だけで済みます。

静的メソッドでも内部的にキャッシュは行われますが、明示的にインスタンスを管理する方が、特に大規模なアプリケーションにおいては予期せぬパフォーマンス低下を防ぐことができます。

置換文字列でのグループ参照(後方参照)

正規表現置換の真骨頂は、パターンの中で一致した特定の部分を 置換後の文字列に再利用できる 点にあります。

これを「後方参照」と呼びます。

番号による参照($1, $2, …)

パターン内でカッコ () を使ってグループ化すると、左から順に $1, $2, $3 という変数のように参照できます。

C#
string input = "田中 太郎";
string pattern = @"(\w+)\s+(\w+)";
string replacement = "$2 $1";

string result = Regex.Replace(input, pattern, replacement);
// 結果: 太郎 田中

この例では、名字と名前を入れ替えています。

$0 は一致した文字列全体を指します。

名前付きグループによる参照(${name})

グループに名前を付けることで、コードの可読性を劇的に向上させることができます。

複雑な正規表現を扱う場合は、番号よりも名前付きグループの使用が推奨されます。

C#
string input = "2024-05-20";
string pattern = @"(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})";
string replacement = "${year}年${month}月${day}日";

string result = Regex.Replace(input, pattern, replacement);
// 結果: 2024年05月20日

名前付きグループを使用するには、(?<名前>パターン) という構文を使い、置換文字列側では ${名前} と記述します。

これにより、後からパターンを変更した際にグループの番号がずれても、置換処理が壊れるリスクを減らすことができます。

MatchEvaluator による動的な置換

単純な文字列の置き換えだけでなく、「一致した内容に応じて計算やロジックを適用したい」 という場面があります。

そのような時に強力なのが MatchEvaluator デリゲートです。

MatchEvaluator を使用すると、置換の各ステップで独自のメソッドやラムダ式を実行できます。

C#
string input = "価格は 100 円、送料は 500 円です。";
string pattern = @"\d+";

// 一致した数値を2倍にして置換する
string result = Regex.Replace(input, pattern, m => {
    int value = int.Parse(m.Value);
    return (value * 2).ToString();
});

// 結果: 価格は 200 円、送料は 1000 円です。

この手法を使えば、データベースの値を参照して置換したり、大文字・小文字を複雑に変換したりといった、高度な文字列処理が可能になります。

Match オブジェクト m を通じて、一致した文字列のインデックスや長さなどの詳細情報にもアクセスできます。

RegexOptions で動作を制御する

正規表現の挙動は、RegexOptions 列挙型を指定することで細かく制御できます。

置換処理においてよく使われるオプションを紹介します。

オプション名内容
IgnoreCase大文字と小文字を区別せずに検索・置換する。
Multiline^$ が各行の始まりと終わりに一致するようにする。
Singleline.(ドット)が改行文字を含むすべての文字に一致するようにする。
IgnorePatternWhitespaceパターン内の空白を無視し、コメント(#)を許可する。
Compiled正規表現を MSIL にコンパイルし、実行速度を向上させる(初期化は遅くなる)。

具体的な使用例(大文字小文字の無視)

C#
string input = "The quick brown fox jumps over THE lazy dog.";
string pattern = "the";
string replacement = "a";

// IgnoreCase を指定して、"The" も "THE" も置換対象にする
string result = Regex.Replace(input, pattern, replacement, RegexOptions.IgnoreCase);
// 結果: a quick brown fox jumps over a lazy dog.

パフォーマンスの最適化と Source Generator

現代の C# 開発(特に .NET 7 以降)において、正規表現のパフォーマンスは劇的に進化しました。

従来の RegexOptions.Compiled も有効ですが、現在は Source Generator を活用した手法が推奨されています。

[GeneratedRegex] 属性の活用

.NET 7 以降では、partial メソッドに [GeneratedRegex] 属性を付与することで、ビルド時に正規表現の解析コードを自動生成できます。

これにより、実行時のオーバーヘッドがほぼゼロになります。

C#
public partial class StringHelper
{
    // ビルド時に正規表現エンジンが生成される
    [GeneratedRegex(@"\d{3}-\d{4}")]
    private static partial Regex ZipCodeRegex();

    public static string MaskZipCode(string input)
    {
        return ZipCodeRegex().Replace(input, "###-####");
    }
}

このアプローチは、特にクラウドネイティブな環境や AOT(Ahead-Of-Time)コンパイルを必要とするモバイルアプリ・WebAssembly において、起動時間の短縮とメモリ使用量の削減に大きく貢献します。

実践的な置換シナリオ

ここでは、実務でよく遭遇する具体的な置換のケーススタディをいくつか紹介します。

HTMLタグの除去

Web フォームなどから受け取った文字列から HTML タグをすべて取り除き、プレーンテキストにしたい場合があります。

C#
string html = "<p>こんにちは、<b>世界</b>!</p>";
string cleanText = Regex.Replace(html, "<.*?>", string.Empty);
// 結果: こんにちは、世界!

<.*?> というパターンは「非強欲(Non-greedy)」なマッチングを行い、最短のタグ一致を繰り返して削除します。

電話番号のフォーマット統一

ユーザー入力によってバラバラな電話番号の形式(ハイフンあり・なし)を統一します。

C#
string input = "09012345678";
string pattern = @"(\d{3})(\d{4})(\d{4})";
string result = Regex.Replace(input, pattern, "$1-$2-$3");
// 結果: 090-1234-5678

重複する空白の集約

文章内の連続した空白やタブを、1つの半角スペースにまとめます。

C#
string text = "これは    テスト   文章です。";
string result = Regex.Replace(text, @"\s+", " ");
// 結果: これは テスト 文章です。

まとめ

Regex.Replace は、C# における文字列操作の中でも非常に柔軟かつ強力なメソッドです。

単純な置換から、後方参照を利用したパターンの組み換え、MatchEvaluator による動的処理まで、その用途は多岐にわたります。

最後に、利用時のポイントをおさらいしましょう。

1
メソッドの選択

一度きりの置換なら静的メソッド、繰り返し利用ならインスタンスメソッドを選びます。

2
後方参照の活用

置換後の文字列にマッチ内容を含めたい場合は、$1${name} などの後方参照を活用します。

3
複雑なロジックへの対応

複雑なロジックが必要な場合は、ラムダ式を用いた MatchEvaluator を検討します。

4
パフォーマンスの最適化

.NET 7 以降であれば、パフォーマンス向上のために Source GeneratorGeneratedRegex) の利用を優先します。

正規表現は習得するまで少し時間がかかりますが、一度マスターすれば文字列処理の生産性は飛躍的に向上します。

ぜひ本記事を参考に、日々のコーディングに役立ててください。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!