ここまでは、Regexクラスから検索とキャプチャを行うMatchメソッドだけを利用してきたが、Regexクラスにはこのほかにも幾つか便利なメソッドが用意されている。ここではその中から、正規表現にマッチした文字列の置換に利用するReplaceメソッドを解説する。
■置換パターン
正規表現による検索を行うと同時に、マッチした部分文字列を指定したルールで置換するメソッドがRegexクラスのReplaceメソッドである。StringクラスのReplaceメソッドと同種のメソッドだが、正規表現を使える以上に多機能なメソッドだ。
まず、簡単なサンプルをリスト6に示す。このプログラムは、指定されたファイルを読みとり、正規表現にマッチした文字列を“< 〜 >”で囲んで出力するというものだ。
using System;
using System.IO;
using System.Text.RegularExpressions;
class Grep {
public static void Main(string[] args) {
TextReader reader = null;
try {
if (args.Length < 1) {
Console.WriteLine(
"replace <RegularExpression> [<filesname>]");
} else if (args.Length < 2) {
reader = Console.In;
} else {
reader = new StreamReader(args[1]);
}
if (reader != null) {
Regex regex = new Regex(args[0]);
string line;
while ((line = reader.ReadLine()) != null) {
string s = regex.Replace(line, "<$&>");
Console.WriteLine(s);
}
reader.Close();
}
}
catch (Exception e) {
System.Console.WriteLine(e.ToString());
}
}
}
例えば、以下のように“\w+”を指定すれば、各ワードが不等号で囲まれて出力される。Replaceメソッドによって置換されるのは一致した部分文字列だけで、残りの部分はそのまま残されるので、おおむねソース・リストの形を保った状態で出力される。
replace "\w+" replace.cs
この実行結果は次のようになる。
<using> <System>;
<using> <System>.<IO>;
<using> <System>.<Text>.<RegularExpressions>;
<class> <Grep> {
<public> <static> <void> <Main>(<string>[] <args>) {
<TextReader> <reader> = <null>;
<try> {
<if> (<args>.<Length> < <1>) {
<Console>.<WriteLine>(
"<replace> <<RegularExpression>> [<<filesname>>]");
} <else> <if> (<args>.<Length> < <2>) {
<reader> = <Console>.<In>;
} <else> {
<reader> = <new> <StreamReader>(<args>[<1>]);
}
[以下省略]
Replaceメソッドの利用手順は、以下のようになる
リスト6では、置換パターンに「<$&>」が指定されている。この“$”と1文字の組み合わせが、置換パターンでのみ指定できる特別なパターンである。正規表現のようでもあるが正規表現ではない。
置換パターンに指定された“$&”は、「正規表現に一致した文字列全体」を表すパターンである。この働きのおかげで、先ほどは「while」が「<while>」に変換されたわけだ。正規表現でもそうだったが、置換パターンにおいても“<”と“>”に特殊な意味はないので、そのまま置換後の文字として出力される。
この種の置換パターンには“$&”以外にも、次の表に示すものが用意されている。
置換パターン | 機能 |
---|---|
$n | n番目のグループでキャプチャされた文字列で置換される(Groups[n].Valueに相当) |
$$ | “$”そのものに置換される |
$& | 正規表現に一致した文字列全体に置換される |
$+ | 最後のグループにキャプチャされた文字列に置換される |
$_ | 入力文字列全体に置換される |
置換パターンの一覧 |
■MatchEvaluatorデリゲート
上表に示す置換パターンでは表現できないような、もっと複雑なルールに基づいた置換を行う方法も用意されている。例えば、正規表現にマッチした文字列に含まれる英字をすべて大文字に変換したいとしよう。置換パターンでは、一致した部分文字列を参照して、並べ替えることしかできないので、この種の置換は表現できない。そこで用意されているのが、置換パターンの代わりに、置換メソッドを指定する方法だ。
置換パターンの場合は、マッチする文字列が見つかるたびに、置換パターンに沿って置換が行われるが、第2の方法では代わりに置換メソッドが呼び出される。置換メソッドは次のシグネチャで表されるMatchEvaluatorデリゲートとして宣言し、Replaceメソッドに渡す。
string MatchEvaluator(Match match)
この置換メソッドには検索結果が格納されたMatchオブジェクトが渡されるので、これを参照して置換後の文字列を柔軟に作り出すことができる。
それでは、一致文字列を大文字に置換するプログラムを示そう(リスト7)。
using System;
using System.IO;
using System.Text.RegularExpressions;
class MatchEval {
public static string RegexMatchEvaluator(Match match) {
return match.Value.ToUpper();
}
public static void Main(string[] args) {
TextReader reader = null;
try {
if (args.Length < 1) {
Console.WriteLine(
"grep <RegularExpression> [<filesname>]");
} else if (args.Length < 2) {
reader = Console.In;
} else {
reader = new StreamReader(args[1]);
}
if (reader != null) {
Regex regex = new Regex(args[0]);
string line;
while ((line = reader.ReadLine()) != null) {
string s = regex.Replace(line,
new MatchEvaluator(RegexMatchEvaluator));
Console.WriteLine(s);
}
reader.Close();
}
}
catch (Exception e) {
System.Console.WriteLine(e.ToString());
}
}
}
プログラムのほとんどはリスト6と同じだが、Replaceメソッドの呼び出しが以下のように修正され、MatchEvaluatorデリゲートとしてRegexMatchEvaluatorメソッドが渡されている。
string s = regex.Replace(line, new MatchEvaluator(RegexMatchEvaluator));
リスト7では、RegexMatchEvaluatorメソッドが次のように宣言されている。
public static string RegexMatchEvaluator(Match match) {
return match.Value.ToUpper();
}
パラメータのMatchオブジェクトには、Matchメソッドを呼び出したときと同じ情報が格納されているので、これを参照して一致文字列を取得し(match.Value)、ToUpperメソッドを使って大文字に変換する。こうして作った文字列をメソッドの戻り値として返せば作業は終了だ。もしReplaceメソッドの処理中に、複数の文字列がマッチしたときには、そのたびにMatchEvaluatorデリゲートが呼び出されるので、NextMatchメソッドを使う必要はない。
次の例では、このサンプル・プログラムにより括弧内の文字だけをすべて大文字に変換する。
replace2 "\(.*\)" replace2.cs
これを実行すれば、次のような出力が得られる。
using System;
using System.IO;
using System.Text.RegularExpressions;
class MatchEval {
public static string RegexMatchEvaluator(MATCH MATCH) {
return match.Value.ToUpper();
}
public static void Main(STRING[] ARGS) {
TextReader reader = null;
try {
if (ARGS.LENGTH < 1) {
Console.WriteLine(
"grep <RegularExpression> [<filesname>]");
} else if (ARGS.LENGTH < 2) {
reader = Console.In;
} else {
reader = new StreamReader(ARGS[1]);
}
[以下省略]
以上2回に渡り、正規表現入門ということで、正規表現とそれを使った.NET Frameworkクラス・ライブラリによる文字列処理について解説した。しかしリファレンス・マニュアルを見ていただければ分かるように、より複雑なパターンを記述するための、あるいは、より効率よくパターンを記述するための要素や機能がほかにも多数用意されている。本稿が正規表現とクラス・ライブラリをより深く学ぶためのきっかけとなれば幸いである。
Copyright© Digital Advantage Corp. All Rights Reserved.