Tsuの雑記¯\_(ツ)_/¯

主に製作メモ・備忘録として使用。製作したアプリのリンクもあります。

【C#】文字列をマッチした順で個別に置換する【正規表現】

例えば、「あ[い]う[え]お」を「あ<いい>う<ええ>お」と置換する方法です。


始めに

文字列の置換を複雑な条件で行うためには、Regexクラスによる正規表現が必要です。

docs.microsoft.com

今回は、Regexクラスが持つReplace()メソッドで文字列を置換します。

docs.microsoft.com


条件を正しく認識する

冒頭で例示した、「あ[い]う[え]おあ<いい>う<ええ>おに置換する」場合の条件について考えましょう。

これは、「[と最短の]で挟まれた文字を2回繰り返し、[]<>に変える」と言い換える事が出来ます。

つまり、まずは「[と最短の]で挟まれた文字」をパターンマッチで探す事になります。

これを正規表現で記すと、以下のようになります。

@"\[([\s|\S]*?)\]"

@エスケープ文字(次文にて解説)の入力を簡易にするほか、IDEによってはシンタックスハイライトが利いて読みやすくなります。

[]正規表現において特別な役割を持っているため、エスケープ文字\に続けて記す必要があります。

()は、マッチした文字列の中から後で取り出したい部分を指定する場合に必要です。

\sは空白文字以外の文字で、\Sは改行を含む空白文字です。

*は「直前の文字が0回以上続く」事を意味し、更に?を続けて記す事で「直後の文字が最初に表れるまで」と指定できます。

これで、置換したい部分の検索が可能になりました。


マッチした順で個別に置換する

個別置換を実現するためには、MatchEvaluatorをパラメータに取るReplace()メソッドを使います。

docs.microsoft.com

これでパターンにマッチするたび、MatchEvaluatorに代入したメソッドが呼び出されます。

docs.microsoft.com

前項のパターンを用いて、これを実装してみましょう。

using System.Text.RegularExpressions;

class A
{
    void B()
    {
        var str = "あ[い]う[え]お";
        var pattern = new Regex(@"\[([\s|\S]*?)\]");
        str = pattern.Replace(str, new MatchEvaluator((Match match) => $"<{match.Groups[1].Value + match.Groups[1].Value}>"));
        // あ<いい>う<ええ>お
    }
}

Match.Groups[1]で、マッチパターン(@"\[([\s|\S]*?)\]")のかっこで囲んだ部分(([\s|\S]*?))を取得しています。

docs.microsoft.com

これによって、「い」と「え」それぞれの置換が1つずつ順番に行われます。

今回はMatchEvaluator内の処理が単純だったためラムダ式で直接書き込みましたが、独立したメソッドをパラメータにする事も出来ます。

using System.Text.RegularExpressions;

class A
{
    void B()
    {
        var str = "あ[い]う[え]お";
        var pattern = new Regex(@"\[([\s|\S]*?)\]");
        str = pattern.Replace(str, B);
        // あ<いい>う<ええ>お
    }

    string B(Match match)
    {
        return $"<iframe{match.Groups[1].Value}></iframe>";
    }
}


終わりに

正規表現は複雑なパターンにも対応できる反面、正確に記述するのはとても難しいですね。

記事を作成しながら新しい疑問点や気付きもあったので、また正規表現について調べてみたいと思います。

以上、文字列をマッチした順で個別に置換する方法でした。