インクリメンタルなカイハツにっき

.NET 開発手法を中心に、これから始める方の一助となる記事を載せていく予定です。

正規表現を利用して Pascal 形式に変換する - C#

f:id:increment-i:20160311184530j:plain
C#に限らず大概の言語では、正規表現がサポートされており、検索と置換が可能になっています。
いざ利用しようとすると...パターン...忘れた!
なんてことはよくあることです。
そんな時、かなりの確率でお世話になるのがこちらのサイト。

正規表現言語 - クイック リファレンス

ちなみに正規表現とはなんぞやという方はこちらで

wikipedia:正規表現

これだけ見て意味が分かった方は、結構理解力が高いかと...

PascalCase, camelCase, snake_case

正規表現の話をするときりがないので、そこはさらっと流して、今回やりたいことは、Snake 記法を Pascal 記法に変換するという処理です。

Pascal,Camel,Snake 各 Case に関してはこちら。

wikipedia:キャメルケース

例えば、

  • hoge -> Hoge
  • hello_world -> HelloWorld

こんな感じですね。

要は、先頭を大文字にしたいのですが、さて、C# ではどのように組み込めばよいのでしょうか?

Perl の場合

なんか懐かしいと言っては失礼ですが、文字処理の得意な Perl の場合は、

$string = 'hoge';
$string =~ s/^([a-z])/\u$1/;

こんな感じでいけるみたいですね。(確認していないので間違っていたらごめんなさい。ちなみにアンダーバーは考慮していません)
「\u」を指定することにより、先頭文字を大文字に変換するという便利なものがあります。

Regex.Replace

それでは話を戻して C# の場合。
そもそも、正規表現の変換で利用できそうなものは...これですね。

Regex.Replace メソッド (String, String, MatchEvaluator) (System.Text.RegularExpressions)

構文をみると

public string Replace(
	string input,
	string pattern,
	MatchEvaluator evaluator
)

えばりゅえーたー?なんやそれ?
そう思って google翻訳さんに問い合わせてみたところ...
translate.google.co.jp

[英語]evaluator -> [日本語]エバリュエータ

まんまやん!

こういう時は、やっぱりこちらが頼りですね。
ejje.weblio.jp

評価器; エバリュエータ; 評価者

えばりゅえーた、強いな...そんな有名なんか?

...とんでもなく脱線してきたので、話を元に戻します。

MatchEvaluator デリゲート

第3パラメータには、MatchEveluator デリゲート を渡します。

ちなみに、デリゲート?という方は、こちらに説明ありますが、
デリゲート (C# プログラミング ガイド)
要は、メソッドを渡すわけですね。そのメソッドの形がデリゲートとして指定されています。

public delegate string MatchEvaluator(
	Match match
)

MatchEvaluator は、Match オブジェクトをパラメータとし、文字列を返すメソッドです。
すなわち、第1パラメータで文字列を指定して、第2パラメータで正規表現のパターンを指定します。これに一致したものが、第3パラメータで指定したメソッドの引数に Match オブジェクトとして渡されるわけです。

実際にやってみる

これらを踏まえて実際に処理方法を考えます。パターンはどうしましょうか?
今回は Snake → Pascal という処理なので、

  1. アンダーバーの部分を通常の空白文字にする
  2. 各先頭文字(英数字以外の文字の次の文字)をパターンマッチさせる
  3. パターンマッチした文字を大文字にする
  4. 空白文字を削除する

こんな感じでいけそうです。

アンダーバーの部分を通常の空白文字にする

これは簡単ですね。わざわざ正規表現など使うことなく

string text = "hello_world";
text = text.Replace("_", " ");
// text は hello world

String.Replace メソッド (System)でOKでしょう。

各先頭文字をパターンマッチさせる

先の例題の場合だと、「hello」の「h」と「world」の「w」にマッチするパターンを作成する必要があります。
大丈夫です。ちゃんと用意されています。

  • \b 「\w (英数字) と \W (英数字以外) 文字の境界位置で一致」

hello world」の場合だと、

  • 先頭
  • 「hello」の「o」の後
  • 「world」の「w」の前
  • 最後

境界で一致するので、もうひと手間加えて

\b[a-z]

境界の次が a から z の間(英字)であればとすれば...よさそうですね!

パターンマッチした文字を大文字にする

ここで、いよいよ Evaluator が登場です。
大文字に変換するメソッドを、MatchEvaluator デリゲートとして渡してやれば良いわけですね。
例えば、次のようなメソッドを作成し、それをパラメータとして渡すといった感じです。

private string ToPascal(System.Text.RegularExpressions.Match match)
{
    return match.Value.ToUpper();
}

Matchオブジェクトをパラメータとし、文字列を返すメソッドです。
Match オブジェクトの Value プロパティに、マッチした文字列が渡されるので、その文字を大文字に変換して返します。
大文字への変換は、String.ToUpper メソッド (System)で行えばよいでしょう。

ラムダ式

この程度であれば、(他から利用されることがないという前提で)ラムダ式にしちゃいます。

match => match.Value.ToUpper();
空白文字を削除する

アンダーバーから空白文字に変換したこの空白文字は余分な文字となりますので、削除します。

text.Replace(" ", "");
// text は変換し終えた文字列

ここも、String.Replace メソッド (System)で。

結果

で、長々ときましたが、以上をまとめると...

private static string ToPascal(string text)
{
    return Regex.Replace(
        text.Replace("_", " "), 
        @"\b[a-z]", 
        match => match.Value.ToUpper()).Replace(" ", "");
}

試しに、こんな感じで実行すると...

static void Main()
{
   Console.WriteLine(ToPascal("hogehoge"));
   Console.WriteLine(ToPascal("hello_world"));
}
// 出力
// Hogehoge
// HelloWorld

できました。

ちなみにこの方法、C#以外でも大体同じような感じです。

PHP: preg_replace_callback - Manual

理屈さえ理解できれば、あとは何とでも対応可能でしょう。