【ワレコの講座】正規表現の最短マッチは便利だ

この記事は約8分で読めます。
スポンサーリンク

例えばこんな住所の文字列から先頭の都道府県名を取り出したい場合、

”  東京都武蔵野市吉祥寺南町1丁目  “

C#の正規表現で

“^\s*(\S{2,3}[都道府県]).*$”

と書けば、正規表現の取得結果の一番目のキャプチャ

$1

で、

東京都

が取り出せると思ったのだが、少し問題があった。

 

記号の意味は以下の通り。

記号 意味
^  文字列の先頭から
\s*  0個以上の空白があって
(\S{2,3}[都道府県])  空白以外の文字が2~3個あって、その次に[都道府県]のどれかの一文字がある。それを ( ) で記憶して、あとで $1 で取り出す。
.*  任意の1文字が0個以上あって
$  文字列の末尾に一致

と言う感じかな。

{2,3} の理由は、都道府県名に2文字や3文字の場合があるからだ。

2文字の名前 東京、千葉、茨城、、、

3文字の名前 鹿児島、神奈川、和歌山、、

 

ところが問題があった。

その問題を解決するために、正規表現の最小マッチを使うと上手く行った。

 

この記事では、正規表現の最小マッチに付いての理解を深める。

まあ、ワテの備忘録である。

では本題に入ろう。

スポンサーリンク
ワテ推薦のプログラミングスクールで学ぶ
スポンサーリンク
スポンサーリンク

「東京都府中市」が「東京都府」と誤認される問題

例えば、

”  東京都府中市府中町1丁目  “

にこの正規表現

  “^\s*(\S{2,3}[都道府県]).*$”

を適応すると、$1は

“東京都府”

にマッチしてしまう。

 

つまり {2,3} というパターンの場合、2や3は量指定子と呼ばれるが、通常、量指定子は最長一致になる。要するに、2文字でも3文字でもパターンにマッチする場合は、多いほうの3文字でマッチさせる動作をするのだ。

2文字 東京 + 都     <==こちらを期待しているのだが、

3文字 東京都 + 府    <==実際はこちらにマッチしてしまう。

最長マッチ、欲張りマッチ、強欲マッチなどと呼ばれるようだ。

 

困った。

最小マッチ

最大マッチがあるなら、最小マッチもあるだろう。実際にある。

量指定子に ? 文字を付けると最短一致になるのだ。

?

最短一致の場合は、繰り返しができるだけ少なくなるようにマッチさせる動作となる。

今の例の場合には、

(\S{2,3}[都道府県])

(\S{2,3}?[都道府県])

に変更すると、「東京都府」にはマッチさせずに「東京都」にマッチした時点で次の文字の処理に進むのだ。

全体のパターンを書くと、

“^\s*(\S{2,3}?[都道府県]).*$”

となる。

これで無事に「東京都府中市・・・」の場合も期待通り「東京都」が取り出せた。

この例以外に、そういうパターンはあるのか気になる。

調べた限りでは、もう一つ、

宮崎県都城市・・・

が有った。これ以外に有るのかどうかは未確認です。

ちなみに、

石川県野々市市・・・

という地名がある。

この場合には、もし市名を取得する正規表現で最短マッチを使うと

野々市

を取得してしまう。つまり「ののいち市」ではなく「のの市」と誤認してしまう。

なので、今回の問題とは逆の問題が起こる。

市名の取得の場合は、通常の最長マッチが良いのかな。たぶん。

最小マッチの動作の実例

正規表現で ? は、通常は、直前文字の 0 回または 1 回一致だが、?を量指定子の後ろに付けると最小マッチの動作になる。

この点が重要だ。

では、実際の例で見てみよう。

猫?  文字の後ろに?がある場合

このパターン「猫?」なら文字「猫」が0個か1個にマッチする。もし「猫」がある場合には正規表現の最大マッチの動作の結果、その「猫」はマッチされる。

その例を正規表現の文字列置換で試してみた。

置換前 犬と猫がいて僕がいる。 検索文字列 犬と猫?
置換後 象がいて僕がいる。 置換文字列

赤文字が検索文字列パターンにマッチする部分を示す(以下同じ)。

この例では「犬と猫」が「象」に置換された。

? は {0,1} と同じ意味になるのでパターンを以下の様に書いても同じ結果になる。実際にやってみる。

猫{0,1}     は猫?と同じ

この場合も、上の例と同じく文字「猫」が0個か1個にマッチする動作だ。

置換前

犬と猫がいて僕がいる。

検索文字列 犬と猫{0,1}
置換後 象がいて僕がいる。 置換文字列

しかし、ここで最小マッチが登場すると、その動作が変わる。

猫{0,1}?    で最小マッチ

猫{0,1}だけなら、上例と同じく文字「猫」が0個か1個に最大マッチする動作であるが、この場合には量指定子{0,1}の後ろに最小マッチの?が付いている。その結果、もし「猫」があってもマッチせずに読み飛ばす動作だ。

置換前 犬と猫がいて僕がいる。 検索文字列

犬と猫{0,1}?

置換後 象猫がいて僕がいる。 置換文字列

この様に、最小マッチ記号?が量指定子の直後にある場合には、上二つの例とは異なる結果になるので、その点を十分理解しておくと良いだろう。

主な量指定子に最小マッチの?を付けた場合の動作

主な例は以下の通り。

最長一致の量指定子 最短一致の量指定子 description
* *? 0 回以上の繰り返しに一致
+ +? 1 回以上の繰り返しに一致
? ?? 0 回または 1 回の繰り返しに一致
{n} { n }? n の繰り返しに一致
{ n ,} { n ,}? 少なくとも n の繰り返しに一致
{ n , m } { n , m }? n から m の繰り返しに一致

この中でも??を覚えておくと良いかも。

?だけなら、0 回または 1 回の繰り返しに一致する場合は、1 回の繰り返しにマッチする動作だ。

でも、??なら仮に1回の繰り返し動作が可能であっても、0回の繰り返しにマッチする(つまりマッチせずに読み飛ばす)動作なのだ。

そう言う事か!

 

ついでに参考までに良く使う正規表現の記号を紹介しよう。

これくらい覚えておけば、まあ、ある程度の文字列処理作業をこなす事が可能だ。

と言うか、ワテはこれくらいしか知らん。

良く使う正規表現の記号

Microsoft C# の Regexの場合

文字  説明
^

入力文字列の先頭と一致。

Multiline プロパティを設定すると、’\n’ または ‘\r’ の直後とも一致。

\s

空白、タブ、フォーム フィードなどの任意の空白文字と一致。

[ \f\n\r\t\v] と同じ意味。

\S

空白文字以外の任意の文字と一致。

[^ \f\n\r\t\v] と同じ意味。

*

直前の文字または部分式と 0 回以上一致。

* は {0,} と同じ意味。

(pattern)

pattern と一致した文字列を記憶する部分式。

一致する文字列が見つかったら記憶され、その部分は Matches コレクションから $0…$9 プロパティを使用して取得できる。

かっこ ( ) と一致させるには、’\(‘ または ‘\)’ を指定する。

{2,3}

直前の文字の 2 ~ 3 回の繰り返しに一致。

コンマと数の間には空白を入れない。

[都道府県] 角かっこで囲まれた文字のいずれかに一致。
 .

“\n” を除く任意の 1 文字に一致。

‘\n’ を含めて任意の文字と一致させるには、'[\s\S]’ などのパターンを指定。

 $

入力文字列の末尾と一致。

Multiline プロパティが設定されている場合は、’\n’ または ‘\r’ の直前とも一致。

 

まとめ

正規表現は、奥が深い。

正規表現の凡人のワテの場合、正規表現の達人への道のりは遠い。

そう言う場合は、こんな映画を見るかな。

アチョ~ォ

 

正規表現の関連書籍

正規表現と言えば、このフクロウの本が有名だ。

500ページ以上もある。

よくこんな本が書けると思う。

 

ネズミの本もあるのか。

イタチかもしれない。気になる。

 

そんな事より正規表現を覚えよう。

 

関連のお勧め記事

【ワテの備忘録】C#の正規表現MatchとMatchesの違いが分からん【解決】

 

ドリル・・・懐かしい。

小学校でやったなあ。

 

ドリルと言えば、日立工機のコードレスがお勧めだ。

小型軽量でパワフル。

ドライバー、ドリルの両方が使えるのでネジ締め、穴あけどちらも可能。

DIY好きな人にお勧めしたい。

スポンサーリンク
ワテ推薦のプログラミングスクールで学ぶ
コメント募集

この記事に関して何か質問とか補足など有りましたら、このページ下部にあるコメント欄からお知らせ下さい。

C# 正規表現
スポンサーリンク
warekoをフォローする
スポンサーリンク
われこ われこ

コメント