[C#] 正規表現を使った文字列の置換方法 [Regex.Replace]

C#

この記事では、C#での正規表現による文字列の置換方法を解説します。

 

文字列操作の基本である置換処理。

置換処理をするにあたって、まず思い浮かべるのはString.Replaceメソッドかと思います。

しかし、いざ処理を書いてみると、String.Replaceメソッドでは対応し辛い場面というのは結構あるものです。例えば、以下のような状況が挙げられます。

  • 文字列の中の数字をまとめて置換したい
  • アルファベットの大文字と小文字を無視して置換したい
  • 単語単位で置換したい(部分一致は無視したい)
  • 日付のフォーマットを変換したい
  • 余分なスペースを一つにまとめたい
  • 複数回登場する単語の、最初にマッチした部分だけを置換したい


そういう時には、正規表現を使った置換処理を使うと良いでしょう。

ひつじさん
ひつじさん

正規表現の置換は便利です。覚えたほうがいいです

 

概要

正規表現とは

正規表現(Regex:Regular Expression)とは、特定の文字列のパターンを検索・抽出・置換するためのルールを記述する方法です。

正規表現を使えば、「数字のみ」「アルファベットのみ」といった広い範囲で対象を指定できるほか、URLメールアドレスなどの特定のフォーマットに沿った文字列も処理しやすくなります。
記法は少し複雑ですが、使いこなせれば非常に便利です。

今回は、正規表現の置換処理にあたるRegex.Replaceメソッドについて解説します。

 

正規表現は便利ですが、複雑な記号の組み合わせで構成されるため可読性が低く、間違っていてもコンパイルエラーにならないためバグを発生させやすい一面もあります。

また、ユーザー入力をそのまま正規表現パターンに使うと、意図的に処理が遅くなる入力をされるセキュリティリスクがあります。このような攻撃をReDoSと言います。

便利だから使いまくればいいというわけではないので注意が必要です。

 

正規表現のパターン

以下に正規表現パターンの一部を紹介します。

パターン説明
\d数字(0-9)
\D数字以外
\s空白(スペース、タブ、改行)
\S空白以外
\b単語の境界
*直前のパターンの0回以上の繰り返し
+直前のパターンの1回以上の繰り返し
?直前のパターンの0回または1回の繰り返し
()グループ化
[0-9]数字0~9の範囲

 

このように正規表現には様々なパターンがあります。
実際に使うときは、これらのパターンを組み合わせて構成するわけですが、記号だけ見てもよくわからないと思いますので、次項ではサンプルと共に正規表現による置換の使用例を解説します。

 

サンプル&詳細解説

数字のみを置換

以下は文字列の中の数字部分を「X」に置き換える処理です。

サンプルコード

using System;
using System.Text.RegularExpressions;

namespace SampleCode
{
    class SampleCode
    {
        static void Main()
        {
            string str = "123ABC4DEF56";

            // 0~9を「X」に置換
            string replaced = Regex.Replace(str, @"[0-9]", "X");

            // 置換後の文字列を出力
            Console.WriteLine(replaced);
            Console.ReadKey();
        }
    }
}

実行結果

XXXABCXDEFXX

「123ABC4DEF56」の数字部分が「X」に置き換わりました。

Regex.Peplaceメソッドの第一引数に元の文字列、第二引数に正規表現パターン(0から9)、第三引数に置き換える文字(X)を指定しています。

 

アルファベットの大文字小文字を無視して置換

文字列の中に含まれる単語の大文字小文字を無視してまとめて置換したいときは、Regex.Peplaceメソッドのオプション引数に「RegexOptions.IgnoreCase」を指定します。

サンプルコード

using System;
using System.Text.RegularExpressions;

namespace SampleCode
{
    class SampleCode
    {
        static void Main()
        {
            string str = "Cat cat CAT Dog";

            // 「Cat」を「XXX」に置換(大文字・小文字は無視)
            string replaced = Regex.Replace(str, @"cat", "XXX", RegexOptions.IgnoreCase);

            // 置換後の文字列を出力
            Console.WriteLine(replaced);
            Console.ReadKey();
        }
    }
}

実行結果

XXX XXX XXX Dog

大文字・小文字関係なく、「Cat」が「XXX」に置換されました。

 

単語単位での置換(部分一致は無視)

部分一致は除外して単語単位で置換したい場合は、「\b」メタ記号を使いましょう。

置換したい単語の前後に「\b」を記述すれば、スペースなどで単語が区切られているもののみを置換します。

サンプルコード

using System;
using System.Text.RegularExpressions;


namespace SampleCode
{
    class SampleCode
    {
        static void Main()
        {
            string str = "Cat AND Dog AND CatDog";

            // 「Cat」を「XXX」に置換
            string replaced = Regex.Replace(str, @"\bCat\b", "XXX");

            // 置換後の文字列を出力
            Console.WriteLine(replaced);
            Console.ReadKey();
        }
    }
}

実行結果

XXX AND Dog AND CatDog

単語単位で区別しているので、「Cat」は「XXX」に置換して「CatDog」は置換しない結果となりました。

 

日付のフォーマットを変換

正規表現を使えば、日付のフォーマットを変換することもできます。

以下のサンプルは、文字列中の日付「yyyy/MM/dd」を「dd-MM-yyyy」にフォーマット変換しています。

サンプルコード

using System;
using System.Text.RegularExpressions;


namespace SampleCode
{
    class SampleCode
    {
        static void Main()
        {
            string str = "My date of birth is 2000/08/10";

            // 日付のフォーマットを置換 yyyy/MM/dd -> dd-MM-yyyy
            string replaced = Regex.Replace(str, @"(\d{4})/(\d{2})/(\d{2})", "$3-$2-$1");

            // 置換後の文字列を出力
            Console.WriteLine(replaced);
            Console.ReadKey();
        }
    }
}

実行結果

My date of birth is 10-08-2000

ここでポイントとなるのは、( )$の使い方です。

正規表現パターンの( )で囲った部分は、一つのグループとして認識されます。
サンプルでは、(\d{4})/(\d{2})/(\d{2}) と記述することで年月日をそれぞれグループ分けしています。なお、\dは数字、{4}や{2}は文字数を示しています。

第三引数で指定している「$3」「$2」「$1」は、正規表現にマッチした部分の番号を表しています。
$1は一番最初にマッチした「2000」、$2は2番目にマッチした「08」、$3は3番目にマッチした「10」を示しています。
そして順番を3-2-1に入れ替えることで、フォーマットを変換しているわけです。

 

余分なスペースを一つにまとめる

不規則にスペースが入った文章を整形したい時にも正規表現を使うと便利です。

サンプルコード

using System;
using System.Text.RegularExpressions;

namespace SampleCode
{
    class SampleCode
    {
        static void Main()
        {
            string str = "Cat          AND  Dog       AND   CatDog";

            // 一つ以上の連続したスペースを一つのスペースに置換
            string replaced = Regex.Replace(str, @"\s+", " ");

            // 置換後の文字列を出力
            Console.WriteLine(replaced);
            Console.ReadKey();
        }
    }
}

実行結果

Cat AND Dog AND CatDog

無駄なスペースを整形され、ひとつにまとめられました。

正規表現パターン「\s」は空白文字を、「+」は1回以上の繰り返しを示しますので、「\s+」で1つ以上の空白文字が置換対象となります。

 

最初にマッチした部分のみ置換

String.Replaceでは文章中に複数回登場する単語は一括で置換されますが、Regex.Replaceであれば回数を指定して置換できます。
テキストエディタの置換機能のように、先頭から1つずつ順番に置換する処理に使えます。

サンプルコード

using System;
using System.Text.RegularExpressions;

namespace SampleCode
{
    class SampleCode
    {
        static void Main()
        {
            string str = "Cat Dog Cat Cat Cat";

            // 最初にマッチした「Cat」を「XXX」に置換
            Regex regex = new Regex(@"Cat");
            string replaced = regex.Replace(str, "XXX", 1);

            // 置換後の文字列を出力
            Console.WriteLine(replaced);
            Console.ReadKey();
        }
    }
}

実行結果

XXX Dog Cat Cat Cat

計4回登場する「Cat」の内、最初の1回だけが「XXX」に置換されました。

Replaceのインスタンス・メソッドでは、引数で置換回数を指定できます。
静的メソッドのReplaceでは回数指定できませんので、サンプルのようにRegexのインスタンス(実体)を作成しましょう。

 

String.Replaceによる文字列置換方法(関連リンク)

String.Replaceメソッドは正規表現ほど柔軟な置換はできませんが、その分シンプルで使いやすいです。具体的な使い方は以下の記事を参考にしてください!

まとめ

以下は当記事のまとめです!

  • Regex.Replaceを使えば、String.Replaceよりも複雑な条件で置換できる。
  • オプション引数RegexOptions.IgnoreCaseで大文字・小文字を無視できる。
  • Regex.Replaceのインスタンスメソッドなら、引数で置換回数を指定できる。
  • 正規表現は可読性が低くエラーを発見し辛いなどの欠点がある。
  • ReDos(正規表現を使ったDOS攻撃)のリスクがある。

 

正規表現を使えば、様々なパターンに対応した柔軟な置換処理を作れるようになります。
使いどころには気を付けつつ、しっかり使いこなせるようになりましょう!

コメント

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