C#で柔軟な文字列操作を実現する正規表現(Regular Expression)は、パターンマッチングや文字列抽出、置換などを効率的に行えます。本記事ではSystem.Text.RegularExpressions名前空間にあるRegexクラスを中心に、基本的な使い方から実践的なテクニック、パフォーマンス最適化までを解説します。
正規表現とは
正規表現とは、文字列のパターンを表現するための“言語”です。
- 電話番号やメールアドレスの形式チェック
 - ログファイルからの特定文字列抽出
 - 複雑な置換処理
 
といった用途に用いられ、プログラミング言語を問わず幅広く採用されています。
System.Text.RegularExpressions名前空間
C#では標準ライブラリとして正規表現機能を提供する以下の名前空間があります。
using System.Text.RegularExpressions;
その中でも主役となるのはRegexクラス。パターン定義とマッチング処理を担います。
Regexクラスの基本メソッド
Regexクラスには正規表現機能の主要な静的メソッドが揃っています。
Regex.IsMatch(string input, string pattern)
パターンにマッチするかどうかをboolで返します。Regex.Match(string input, string pattern)
最初にマッチした部分をMatchオブジェクトで取得します。Regex.Matches(string input, string pattern)
マッチしたすべてのMatchオブジェクトをMatchCollectionで取得します。Regex.Replace(string input, string pattern, string replacement)
マッチ部分を置換した文字列を返します。Regex.Split(string input, string pattern)
パターンを区切り文字として分割した文字列配列を返します。
正規表現パターンの基本構文
正規表現では以下のような記号を利用して文字列のパターンを表現します。
文字クラス・メタ文字
文字列中の数字や英単語を検出する基本構文です。
| パターン | 説明 | 
|---|---|
| \d | 数字(0-9) | 
| \w | 単語に使用される文字 (英数字やアンダースコア)  | 
| \s | 空白文字(スペース) | 
| \S | 非空白文字 | 
| . | 改行以外の任意の一文字 | 
using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        string input = "商品コードはA123、B456です";
        string pattern = @"\w\d+"; // 英字 + 数字の組み合わせ
        foreach (Match m in Regex.Matches(input, pattern))
        {
            Console.WriteLine(m.Value); // A123, B456
        }
    }
}
繰り返し指定
特定の文字の出現回数を指定して検索します。
| パターン | 説明 | 
|---|---|
| * | 0回以上の繰り返し | 
| + | 1回以上の繰り返し | 
| ? | 0回か1回 | 
| {m,n} | m回からn回の繰り返し | 
using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        string input = "aaa ab aaaaaa a";
        string pattern = @"a{2,5}"; // aが2〜5回繰り返されるパターン
        foreach (Match m in Regex.Matches(input, pattern))
        {
            Console.WriteLine(m.Value); // aaa, aaaaa
        }
    }
}
位置指定子
行頭や行末、単語の境界などの位置に応じて検索します。
| パターン | 説明 | 
|---|---|
| ^ | 行頭 | 
| $ | 行末 | 
| \b | 単語の境界 | 
using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        string input = "Hello world\nworld peace\npeace world";
        string pattern = @"^world"; // 行頭にある "world"
        foreach (Match m in Regex.Matches(input, pattern, RegexOptions.Multiline))
        {
            Console.WriteLine(m.Value); // world(2行目のみヒット)
        }
    }
}
グループ化とキャプチャ
正規表現ではパターン全体にマッチした文字列を取得することができますが、括弧()で囲ってグループ化すると一部分に複数条件を指定したり、括弧で囲われたパターンの部分にマッチした文字列のみを部分的にキャプチャ(取得)することができます。
| パターン | 説明 | 
|---|---|
| () | グループ化(キャプチャあり) ※キャプチャ結果は順番を指定して取り出す  | 
| (?:) | グループ化(キャプチャなし) | 
| (?<name>) (?’name‘)  | 名前付きキャプチャ ※キャプチャ結果は名前を指定して取り出す  | 
using System;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        string input = "氏名: 山田 太郎";
        string pattern = @"氏名:\s(?<LastName>\S+)\s(?<FirstName>\S+)";
        Match match = Regex.Match(input, pattern);
        if (match.Success)
        {
            // キャプチャした内容は Groups で参照できます
            Console.WriteLine($"姓: {match.Groups["LastName"].Value}"); // 山田
            Console.WriteLine($"名: {match.Groups["FirstName"].Value}"); // 太郎
        }
    }
}
逐語的識別子@による文字列リテラルの活用
C#では正規表現パターンを文字列で指定しますが、\を二重に書かずに済む逐語的識別子@を使用すると便利です。
var pattern = @"^\d{3}-\d{4}$"; // 郵便番号形式
通常の文字列リテラルだと"^\\d{3}-\\d{4}$"と書く必要がありますが、@"…"なら直感的に記述できます。
オプション指定
正規表現の挙動を変えるオプションはRegexOptions列挙体で指定します。
| 列挙体 | 説明 | 
|---|---|
RegexOptions.IgnoreCase | 大文字小文字を区別しない | 
RegexOptions.Multiline | ^と$を複数行で適用 | 
RegexOptions.Singleline | .で改行もマッチ | 
RegexOptions.Compiled | 正規表現を事前コンパイルし高速化 | 
var regex = new Regex(@"\d+", RegexOptions.Compiled | RegexOptions.IgnoreCase); 
実践例
実践例①:メールアドレス検証
メールアドレスの形式(先頭が[単語文字、ピリオド、ハイフン]の任意の文字列 + アットマーク[単語文字、ピリオド、ハイフン]の任意の文字列 + 末尾がピリオド、単語文字の文字列)になっているか検証します。
string email = "user@example.com";
string pattern = @"^[\w\.-]+@[\w\.-]+\.\w+$";
if (Regex.IsMatch(email, pattern))
    Console.WriteLine("有効なメールアドレスです");
else
    Console.WriteLine("形式が不正です");
※基本的なチェックには十分ですが、RFC完全準拠には不向きな点に注意してください。
実践例②:数値抽出
文章中の整数(数字の繰り返し)をすべて取り出す例です。
string text = "商品A: 1200円, 商品B: 850円";
foreach (Match m in Regex.Matches(text, @"\d+"))
{
    Console.WriteLine(m.Value);  // 1200, 850
}
パフォーマンスと注意点
- 同じパターンを使用する場合は
static readonly Regexやコンパイルオプションでキャッシュする - 複雑すぎるパターンは実行時間が爆発的に増える可能性あるため注意する(分割を検討する)
 - 外部入力を直接パターンにすると正規表現インジェクションのリスクがある(コードを直接実行される)
 
まとめ
C#の正規表現は多彩な文字クラスやオプションを組み合わせることで、単純な文字列チェックから高度なパース処理までこなせます。逐語的識別子@やRegexOptionsを駆使し、適切にキャッシュやコンパイルを行うことで、可読性とパフォーマンスの両立を図りましょう。正規表現の理解が深まれば、文字列操作の幅が飛躍的に広がります。ぜひ日々の開発で正規表現が有効な場面で積極的に活用してみてください!

  
  
  
  
