例えばこんな住所の文字列から先頭の都道府県名を取り出したい場合、
” 東京都武蔵野市吉祥寺南町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}?[都道府県]).*$”
となる。
これで無事に「東京都府中市・・・」の場合も期待通り「東京都」が取り出せた。
この例以外に、そういうパターンはあるのか気になる。
調べた限りでは、もう一つ、
宮崎県都城市・・・
が有った。これ以外に有るのかどうかは未確認です。
ちなみに、
石川県野々市市・・・
という地名がある。
この場合には、もし市名を取得する正規表現で最短マッチを使うと
野々市
を取得してしまう。つまり「ののいち市」ではなく「のの市」と誤認してしまう。
なので、今回の問題とは逆の問題が起こる。
市名の取得の場合は、通常の最長マッチが良いのかな。たぶん。
でも、ワテの経験では日本語住所を正規表現を使って都道府県名、郡市区名、町村名などに分解するのは、かなり手強い。
複雑な正規表現パターンを書けばある程度は期待通りに分解出来るが、やればやるほど泥沼に嵌る感じだ。
なぜなら、日本の住所は色んな個性があるから。
例えば京都の「上る」、「下がる」と言った住所表記は独特だから、正規表現パターンを書くとしても、それらの文字を検出する必要がある。
京都市役所の住所の例
所在地:京都市中京区寺町通御池上る上本能寺前町488
なので、与えられた住所を要素に分解するなら、国土交通省のサイトにある全国の自治体の住所データを使ったり、あるいは郵政省が公開している全国の郵便番号と住所の対応データなどを使いながら、地道にやるのが良さそうだと思う。正規表現で一発で分解するのはほぼ不可能だろう。
最小マッチの動作の実例
正規表現で ? は、通常は、直前文字の 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好きな人にお勧めしたい。
コメント
京都府「・・・」
なるほど、京都府も該当しますね。良い情報をお寄せ頂きましてありがとうございました。