Archive | 03:33

正規表現

3 12月

何が正規なのか問いたい。問い詰めたい。小1時間問い詰めたい。

 

はい、今日のテーマは正規表現です。

 

サンプル コード: 正規表現.cs

正規表現とは

まずは昨日のおさらいから行きましょう。

  • 正規表現(regular expression)は、文字列のパターン マッチのための簡易言語です。
  • C#や.NETに特有のものではなく、PerlやJavaのものとおおむね共通です。
  • C#から正規表現を使うには、Regexクラスを利用します
  • 詳細はMSDNの以下のページで確認できます。

 

しかし、codezineの記事でも言いましたけど、呼び名としていまいちなんですよねぇ、正規表現って。

元々はコンピューター言語学方面の学術用語です。今となっては、パターン マッチのためにいろいろと追加されていて、本来の意味での正規表現にはなっていないそうですけども。

とりあえず、正規表現 = パターン マッチ と覚えてください。

正規表現の書き方

いくつかの特別な意味を持った記号(. {} \ * + など)以外は、一致させたい文字をそのまま書きます。例えば、abという正規表現は、abを含む文字列に一致します。

ab

abを含む文字列に一致します。abの前後に別の文字があっても構いません。aとbの間に別の文字が入る場合には一致しません。

一致例

abc

stab

不一致例

a

acb

 

この例をC#で書くと、以下のようになります。

var r = new Regex(“ab”);
Console.WriteLine(r.Match(“abc”).Success); // true
Console.WriteLine(r.Match(“enable”).Success); // true
Console.WriteLine(r.Match(“a”).Success); // false
Console.WriteLine(r.Match(“acb”).Success); // false

 

以降では、特殊記号について説明していきましょう。

数量指定

同じ文字の繰り返しを検出したい場合に使える、数量指定用の特殊記号として、 *(アスタリスク)、+(プラス)、? (はてな)、{} (波括弧)などがあります。

ab*a

* (アスタリスク)で、0個以上の同じ文字を表します

一致例

aa

abbba

不一致例

a

ab

ab+a

+ (プラス)で、1個以上の同じ文字を表します

一致例

aba

abbba

不一致例

aa

aca

ab?a

? (はてな)で、0個もしくは1個の文字を表します

一致例

aa

aba

不一致例

abba

aca

ab{2}a

{} で、連続する同じ文字を表します。数字を1つだけ入れると、その個数ぴったりを表します。

一致例

abba

 

不一致例

aba

abbba

ab{2,4}a

{} に、コンマで区切って2つの数字を入れると、最小と最大の数指定できます。

一致例

abba

abbbba

不一致例

aba

abbbbba

 

通常、これらの数量指定は「最大一致」になります。一方、これらの記号の後ろに ? (はてな)をつけることで、「最小一致」パターンも作れます。

var r1 = new Regex(@”.*,”); // 任意の文字の後ろにコンマ
var r2 = new Regex(@”.*?,”); // 
上。ただし、最小一致
var str = “aaa,aaa,aaa,”;

Console.WriteLine(r1.Match(str)); // aaa,aaa,aaa, まで拾われる
Console.WriteLine(r2.Match(str)); // aaa, 
だけ拾われる

 

エスケープ

特殊な意味を持つ記号(. *)自体を検索するためには、特殊記号の前に\ 記号(半角円、フォントによっては逆スラッシュ)をつけます。

\\\.\*

\ の直後に特殊記号を書くことで、特殊記号自身を検索できます。

一致例

\.*

 

不一致例

\a*

\.

 

また、普通は見えない文字(改行やタブ文字)も、\ 記号に続けて nt などの文字を書くことで表現します。主要なものを書くと、以下の通りです。

\t

タブ文字。

\n

改行文字。

\r

キャリッジ リターン(復帰)文字。

\unnnn

Unicodeを直接指定します。nnnnのところに、Unicodeを16進数で記述します。

 

このような、特殊記号/不可視文字を入力するための記法をエスケープ(escape: 逃げ道、避難)と呼びます。

文字クラス

特定の文字ではなく、ある範囲の文字(たとえば、算用数字全部など)と一致するようなパターンを作ることができます。

エスケープ同様、\ 記号に続けて ds などの文字を書くことで、文字クラスを表現します。また、[] (角括弧)中に複数の文字を入れることで、そのいずれかの文字に一致します。

\d+

\ 記号は特別な意味を持ちます。\d や \s など、直後の文字によって意味が変わります。\d は任意の算用数字を表します。

一致例

1234

65536

不一致例

abc

—-

\sx+\s

\s は任意の空白文字を表します。

一致例

y x y

y xxx y

不一致例

yxy

yxxxxy

\w+\s+\w+

\w は単語に使われる文字を表します。

一致例

abc xyz

あいう
えお

不一致例

abcdef

あいうえお

\p{Ps}\w+\p{Pe}

\p{} で特定の Unicode カテゴリーに一致します。Ps は開き括弧、Pe は閉じ括弧です。

一致例

(abc}

abc]

不一致例

|abc|

.abc.

a.*\.

. (ピリオド)は任意の1文字を表します。ピリオド自信を表すためには、\. と書きます。

一致例

abcd.

a(!#$%&'().

不一致例

abcd

a

[,\d]+

[] (各括弧)中に含まれる任意の文字に一致します。

一致例

19,800

12,34,56

不一致例

abcd

あいうえ

^[,\d]+$

^ は文字列の先頭、$ は末尾を意味します。

一致例

19,800

12,34,56

不一致例

-19,800

12,34.56

 

グループ化

パターンの一部分だけ取り出したり、置換したりするために、正規表現内にグループを作ることができます。() (丸括弧)でくくった部分がグループになります。

例えば以下のようなコードを見てみましょう。

var r = new Regex(@”(\d{4})/(\d{2})/(\d{2})”);
var m = r.Match(“2011/12/15”);

foreach (var x in m.Groups)
{
    Console.WriteLine(x);
}

() が3か所あります。マッチ結果(m)のGroupsには、マッチした全体と、() でくくった3か所の結果が格納されています。したがって、実行結果は以下の通りです。

2011/12/15
2011
12
15

 

グループには、名前を付けておくこともできます。(?<id>パターン) というように、?<> をつけます。

var r = new Regex(@”(?<year>\d{4})/(?<month>\d{2})/(?<day>\d{2})”);
var m = r.Match(“2011/12/15”);

Console.WriteLine(m.Groups[“year”]);
Console.WriteLine(m.Groups[“month”]);
Console.WriteLine(m.Groups[“day”]);