C#での正規表現の使用

正規表現の動作について復習が必要な場合は、まずインタラクティブチュートリアルをご覧ください!

C#は、標準.NETフレームワークのSystem.Text.RegularExpressions名前空間のクラスを通じて正規表現をサポートしています。.NET正規表現ライブラリでサポートされている高度な機能には、PCREとの間にいくつかの違いがありますが、両者は構文とパターンの大部分を共有しており、式はC#やその他の言語で使用できます。

以下の例では、コードを試行する場合は、ソースファイルの先頭に次の名前空間をインポートしてください。

using System.Text.RegularExpressions;

逐語的文字列リテラル

C#で正規表現を書くときは、通常の文字列ではなく逐語的文字列を使用することをお勧めします。逐語的文字列は特別なプレフィックス(@)で始まり、C#に文字列内のバックスラッシュと特殊なメタ文字を解釈しないように指示し、それらを正規表現エンジンに直接渡すことができます。

つまり、"\n\w"のようなパターンは解釈されず、他の言語のように"\\n\\w"ではなく@"\n\w"として記述でき、読みやすくなります。

文字列のマッチング

System.Text.RegularExpressions名前空間には、正規表現エンジンのインターフェースをカプセル化し、正規表現を使用してテキストから情報を照合および抽出できるRegexクラスがあります。

正規表現が文字列と一致するかどうかをテストするには、オプションのRegexOptions列挙型セットを受け取る静的メソッドRegex.Match()を使用できます。これは、一致(存在する場合)が見つかった場所に関する情報を含むMatchオブジェクトを返します。

メソッド
Match match = Regex.Match(InputStr, Pattern, RegexOptions)
// 日付文字列と一致させる正規表現を使用してみましょう。 string pattern = @"([a-zA-Z]+) (\d+)"; // RegexOptionsはこの呼び出しに対してはオプションです。 // これについては後で詳しく説明します。 Match result = Regex.Match("June 24", pattern); if (result.Success) { // 確かに、「([a-zA-Z]+) (\d+)」という式は日付文字列と一致します。 // マッチのインデックスを取得するには、Matchオブジェクトの // IndexとLengthの値を読み取ることができます。 // これは、文字列の先頭と末尾で一致するため、[0, 7]を出力します。 Console.WriteLine("Match at index [{0}, {1})", result.Index, result.Index + result.Length); // 完全一致したテキストを取得するには、MatchオブジェクトのValueを読み取ることができます。 // これは「June 24」を出力します。 Console.WriteLine("Match: {0}", result.Value); // マッチのそれぞれを繰り返し処理する場合は、 // 次のMatchオブジェクトを返すMatchオブジェクトのNextMatch()メソッドを呼び出すことができます。 // これは、各マッチを順番に出力します。 while (result.Success) { Console.WriteLine("Match: {0}", result.Value); result = result.NextMatch(); } }

キャプチャグループ

入力文字列全体でグローバル検索を実行し、対応するキャプチャデータを含むすべてのマッチを返す場合は、代わりに静的メソッドRegex.Matches()を使用してMatchCollectionを取得できます。これは、上記の例のように反復処理して処理できます。

メソッド
MatchCollection matches = Regex.Matches(InputStr, Pattern, RegexOptions)
// いくつかの日付文字列からデータを取得する正規表現を使用してみましょう。 string pattern = @"([a-zA-Z]+) (\d+)"; MatchCollection matches = Regex.Matches("June 24, August 9, Dec 12", pattern); // これはマッチの数を出力します Console.WriteLine("{0} matches", matches.Count); // これは、各マッチと、マッチが見つかった入力文字列のインデックスを出力します。 // June 24 at index [0, 7) // August 9 at index [9, 17) // Dec 12 at index [19, 25) foreach (Match match in matches) { Console.WriteLine("Match: {0} at index [{1}, {2})", match.Value, match.Index, match.Index + match.Length); } // 各マッチについて、キャプチャされたグループを読み取ることで、 // キャプチャされた情報を抽出できます。 foreach (Match match in matches) { GroupCollection data = match.Groups; // これは、このマッチでキャプチャされたグループの数を表示します Console.WriteLine("{0} groups captured in {1}", data.Count, match.Value); // これは各マッチの月と日を出力します。覚えておいてください // 最初のグループは常に完全一致したテキストであるため、月は // インデックス1から始まります。 Console.WriteLine("Month: " + data[1] + ", Day: " + data[2]); // コレクション内の各グループにもIndexとLengthメンバーがあり、 // 入力文字列のどこでグループが見つかったかが格納されています。 Console.WriteLine("Month found at[{0}, {1})", data[1].Index, data[1].Index + data[1].Length); }

文字列の検索と置換

もう1つの一般的なタスクは、正規表現を使用して文字列の一部を検索して置換することです。たとえば、古いメールドメインのすべてのインスタンスを置換したり、テキストの順序を入れ替えたりする場合などです。C#では、静的メソッドRegex.Replace()を使用してこれを行うことができます。

置換文字列は、パターン内のキャプチャされたグループへの参照を含む正規表現でも、通常の文字列でもかまいません。

メソッド
string replaced = Regex.Replace(InputStr, Pattern, ReplacementPattern, RegexOption)
// いくつかの日付文字列で日と月の順序を入れ替えてみましょう。 // 置換文字列にもメタ文字(キャプチャされたグループへのバックリファレンス)が含まれていることに注意してください。 // そのため、これも逐語的文字列を使用します。 string pattern = @"([a-zA-Z]+) (\d+)"; // これは文字列をインラインで並べ替え、次のように出力します。 // 24 of June, 9 of August, 12 of Dec // 最初のグループは常に完全一致したテキストであるため、 // 月と日のインデックスは0ではなく1から始まります。 string replacedString = Regex.Replace("June 24, August 9, Dec 12", pattern, @"$2 of $1"); Console.WriteLine(replacedString);

RegexOptions 列挙型

上記の正規表現メソッドでは、それぞれがオプションのRegexOptions引数をとることに注意してください。利用可能なフラグのほとんどは便宜的なもので、正規表現自体に直接記述できますが、場合によっては便利なものもあります。

  • RegexOptions.Compiledは、正規表現エンジンが最初にパターンをコンパイルできるようにすることで、同じパターンを持つ複数の入力文字列の一致を高速化します。オプションが設定されていない場合はデフォルトでオンになります。
  • RegexOptions.IgnoreCaseは、パターンを大文字と小文字を区別しないようにし、異なる大文字小文字の文字列に一致させます。
  • RegexOptions.Multilineは、入力文字列に改行文字(\n)が含まれている場合に必要であり、開始と終了のメタ文字(それぞれ^$)が入力文字列全体の先頭と末尾ではなく、各行の先頭と末尾に一致することを許可します。
  • RegexOptions.RightToLeftは、RTL言語での照合に役立ちます。
  • RegexOptions.Singlelineでは、ドット(.)メタ文字が改行文字(\n)を含むすべての文字に一致するようになります。

パフォーマンスのためのパターンのコンパイル

通常の処理には上記の静的メソッドを使用しますが、同じパターンに対して数百万個の入力文字列をテストする場合は、コンストラクタでパターンを使用してRegexオブジェクトをインスタンス化することで、オブジェクトの割り当てを削減し、パフォーマンスを向上させることができます。

このRegexオブジェクトを使用すると、上記と同じメソッドがすべてオブジェクトで使用できますが、各呼び出しでパターンを再度渡す必要はありません。

詳細については、MSDNの静的メソッドとインスタンスメソッドに関するドキュメントを参照してください。

リンク

C#で正規表現を使用する方法の詳細については、次のリンクを参照してください。