宮崎IT関連勉強会 Advent Calendar 2019 10日目です!
実は、Advent Calendarへの参加がはじめてなので、何を書くかとっても悩みました。
どうせなら宮崎を絡めたいなーとか考えていたらあっという間に自分の番がきてしまいました。(笑)
色々考えた結果、最近改めて正規表現を勉強してみたら忘れていることが多かったので、宮崎県の市町村名を対象に色々説明してみようと思います。
参考にしたサイト
私と正規表現
そもそも私は、正規表現が苦手です。。
はじめて見たときは、「これは何を表しているのだろう。。。」と硬直したことを覚えています。
今でもスラスラ書けと言われても正直、自信はありません(苦笑)
プログラムを書く人は、正規表現って切っても切れない技術かと思います。
初学者の方、さらに理解を深めたい方にとって少しでも役立つと嬉しいです。
実行環境と今回のサンプルデータについて
実行環境
- OS:macOS Mojave 10.14.6
- rbenv:1.1.2
- ruby:2.6.3
私は、普段rubyを書くのでrubyで実行していますが、他の言語でも概ね変わらないかと思います。
※一部、挙動に差があるかもしれません。
今回のサンプルデータ
宮崎県のオフィシャルサイト(県内市町村一覧)より市町村名を抜粋
https://www.pref.miyazaki.lg.jp/kohosenryaku/kense/shichoson/shichosonmap.html
暖かくて、料理も美味しい、自然いっぱいのいいところです。
来たことないかたはぜひ〜!!
宮崎市\n都城市\n延岡市\n日南市\n小林市\n日向市\n串間市\n西都市\nえびの市\n三股町\n高原町\n国富町\n綾町\n高鍋町\n新富町\n西米良村\n木城町\n川南町\n都農町\n門川町\n諸塚村\n椎葉村\n美郷町\n高千穂町\n日之影町\n五ヶ瀬町
正規表現の基本
正規表現について簡単にまとめました。
文字種類に関する正規表現
種類 | 意味 |
---|---|
. (ピリオド) | 何かしらの1文字 |
[hoge]、[^hoge] | カッコ内に細かく文字種を指定。冒頭に^(キャレット)をつけると指定文字以外という意味になる。 |
\w、\W | [a-zA-Z_0-9]と同様。小文字、大文字のアルファベット、アンダーバー、数字を指定。大文字にすると逆の意味になる。 |
\d、\D | [0-9]と同様。数字を指定。大文字にすると逆の意味になる。 |
\s、\S | 空白文字を指定。改行や改ページ、半角スペースなど。大文字にすると逆の意味になる。 |
量指定に関する正規表現
種類 | 意味 |
---|---|
*(アスタリスク) | 0回以上の繰り返し。 |
+(プラス) | 1回以上の繰り返し。 |
?(クエスチョン) | 0または1回ある。 |
{n} | 具体的に数字で指定。n回ある。 |
{n,m} | n以上、m以下 |
量指定の後の? (クエスチョン) |
量指定に? をつけると、最初にマッチした時点で検索を終了する。以下に例を記載 |
量指定後「?」について
<tr><td>boom</td></tr>
上記HTMLに対してHTMLタグ部分のみ(<tr><td></td></tr>
)をマッチさせる場合
例 | 結果 | 説明 |
---|---|---|
<.+> |
<tr><td>boom</td></tr> | 正規表現としては「< + 1文字以上のなにか + >」という意味になる。 つまり最初のtrタグの<と最後のtr閉じタグの>でその中身がすべて「.+」にマッチしている状態 |
<.+?> |
<tr><td></td></tr> | 上記とは異なり<で始まり最初にマッチする>で検索が止まる。 そのため「.+」が各タグ内のアルファベットtrやtdに、マッチするようになる。 |
位置指定に関する正規表現
種類 | 意味 |
---|---|
^(キャレット) | 行頭の指定。 |
$(ドル) | 行末の指定。 |
\b、\B | 単語協会。単語と単語の間にある「単語の切れ目」、単語の両端にある空文字を指す。 ディスプレイ上には表示されない概念的な区切りです。 大文字にすると逆の意味になる。 |
AA(?=BB)、AA(?!BB) | 前方参照。文字列AAを検索、ただし直後にBBがあるもののみ。?= を?! にすると前方参照の否定になる。文字列AAを検索、ただし直後にBBがない。 |
(?<=AA)BB、(?<!AA)BB | 後方参照。文字列BBを検索、ただし直前に文字列AAがあるもののみ?<= を?<! にすると前方参照の否定になる。文字列BBを検索、ただし直前に文字列AAがない。 |
特殊な指定をする正規表現
ここから例題
(1)「市」をすべて
ここで使用するもの
- .(ピリオド):何かしらの文字
- +(プラス):1回以上の繰り返し
- [](カッコ):特定の文字指定
「何かしらの1文字以上の文字列 + 市」がマッチすれば良い。
(2)「川」が含まれている市町村をすべて
ここで使用するもの
- .(ピリオド):何かしらの文字
- *(アスタリスク):0回以上の繰り返し
- 量指定の後の?(クエスチョン):最低限の探索
- [](カッコ):特定の文字指定
- A|B:OR演算子
- (?:):キャプチャしないグルーピング
「0回以上の何かしらの文字 + 川 + 0回以上の何かしらの文字 + 市または町または村」がマッチすれば良い。
(3)「〇〇〇市」のような「3文字+市町村」をすべて
ここで使用するもの
- .(ピリオド):何かしらの文字
- {n}:n回以上の繰り返し
- A|B:OR演算子
- (?:):キャプチャしないグルーピング
「3文字も何かしらの文字 + 市または町または村」がマッチすれば良い。
(4)「町」をすべて抽出し「〇〇町」の〇〇だけをマッチ
ここで使用するもの
- .(ピリオド):何かしらの文字
- +(プラス):1回以上の繰り返し
- AA(?=BB):前方参照。文字列AAを検索、ただし直後にBBがあるもののみ。
「1回以上の何かしらの文字(直後に町が付いているもののみ)」がマッチすれば良い。
サンプル
city_list = "宮崎市\n都城市\n延岡市\n日南市\n小林市\n日向市\n串間市\n西都市\nえびの市\n三股町\n高原町\n国富町\n綾町\n高鍋町\n新富町\n西米良村\n木城町\n川南町\n都農町\n門川町\n諸塚村\n椎葉村\n美郷町\n高千穂町\n日之影町\n五ヶ瀬町"
#(1)「市」をすべて
regex_1 = /.+[市]/ # ここが正規表現
p city_list.scan(regex_1)
# 実行結果 => ["宮崎市", "都城市", "延岡市", "日南市", "小林市", "日向市", "串間市", "西都市", "えびの市"]
#(2)「川」が含まれている市町村をすべて
regex_2 = /.*?[川].*?(?:市|町|村)/ # ここが正規表現
p city_list.scan(regex_2)
# 実行結果 => ["川南町", "門川町"]
#(3)「〇〇〇市」のような「3文字+市町村」をすべて
regex_3 = /.{3}(?:市|町|村)/ # ここが正規表現
p city_list.scan(regex_3)
# 実行結果 => ["えびの市", "西米良村", "高千穂町", "日之影町", "五ヶ瀬町"]
#(4)「町」をすべて抽出し「〇〇町」の〇〇だけをマッチ
regex_4 = /.+(?=町)/ # ここが正規表現
p city_list.scan(regex_4)
# 実行結果 => ["三股", "高原", "国富", "綾", "高鍋", "新富", "木城", "川南", "都農", "門川", "美郷", "高千穂", "日之影", "五ヶ瀬"]
やってみて
正規表現ってすごく便利だなーと改めて思いました。
前方参照や後方参照、量指定に関する記号(.+*
)は貪欲に検索するなど、ふとした時に忘れがちなことを改めて思い出せたのも良かったです。
苦手意識を持たれている方に少しでも役立つと嬉しいです。
ご意見、記事内の誤字脱字などありましたらお気軽にコメントいただけると幸いです。