おひとり

できる限りひとりで楽しむための情報やプログラミング情報など。

正規表現の「.+」とキャプチャを組み合わせて繰り返しを含むパターンを作る

正規表現の強力さには日々助けられています。
今回.+とキャプチャ()を組み合わせて繰り返しを含むパターンを作る方法を紹介します。
.*でも同様に利用できます。

f:id:hitoridehitode:20220223130456p:plain:w350
面倒なタスクを正規表現で撃退する図

※ 正規表現を気軽に試すには以下のサイトがおすすめです。

regex101.com

キャプチャとは?

分かる方は読み飛ばしてください。
.+.*は正規表現の基礎なので、知っている前提とします。

正規表現のキャプチャとは、パターンの中から文字列を「ひとまとめ」にし、切り出す機能です。
例えば、HTMLタグのなかで、TEXT部分のみ取得したい場合を考えて見ましょう。

例えば、以下のHTMLがあるとします。

私はよく<a href="https://awesome.example.com">素敵なサイト</a>にアクセスします。

ここで、タグの中のTEXT部(つまり今回の例では<a>の中の素敵なサイト)を取得するには、以下のようにします。
※簡単のため、パターンは「><に囲まれた文字列」と表現します。

>(.*)<

()をつけると、その部分を切り出(キャプチャ) してくれます。
例えば、pythonを使えば次のようにプログラム中で取得できます。

import re

test_str = "私はよく<a href=\"https://awesome.example.com\">素敵なサイト</a>にアクセスします。"
regex = r">(.*)<"

m = re.search(regex, test_str)
print(m.group(1)) // 素敵なサイト

このように、正規表現のキャプチャ機能とは、ざっくり言うと()で指定した部分を「ひとまとめ」にし、切り出す機能です。

「.+」とキャプチャ「()」を組み合わせる

では、早速「.+」とキャプチャ()を組み合わせて使ってみましょう。
実例を使って考えて見ます。

HTML(XML)のタグにマッチさせる

では、<h1>TEXT</h1>といった、HTML(XML)のタグにマッチするかどうかを判定するシンプルな正規表現を考えて見ます。
.+とキャプチャ()を組み合わせると次のように書けます。

<(.+)>(.*)<\/\1>

※正規表現の機能を説明するためのシンプルな実装にとどめています。厳密にHTMLにマッチする正規表現はもっと考慮すべき点が多くあるため、これより複雑になります。

ポイントは、\1の部分です。それ以前でキャプチャ()した内容を参照しています。

f:id:hitoridehitode:20220223103912p:plain
キャプチャした内容を後で参照できる!

便利で感動しますよね!!
これにより、繰り返しを含むパターンを定義できました!!

置換で使う

正規表現を使った置換はプログラムを書くときに強力。
では、 サクッと.*とキャプチャ()を組み合わせてsetterからgetterを生成してみましょう!

以下は擬似コードですが、setterを担う関数のシグネチャです。

void set_account_id(string account_id)

このコードに正規表現による置換を適用して、setterを生成してみます。
sedでやる場合、以下のようなコマンドになります。

echo "void set_account_id(string account_id)" | sed -E 's/(.*)(.*)\((.*) \2\)/\3 get_\2\(\)/'

では、sedの置換部分s/(.*)(.*)\((.*) \2\)/\3 get_\2\(\)/に注目して考えて見ます。
まずは、パターンにマッチさせるための、前方の(.*)(.*)\((.*) \2\)の部分です。

f:id:hitoridehitode:20220223105939p:plain
繰り返しの部分を「.*」とキャプチャ「\2」でパターンを表現

関数名と引数名に同じパターンがありますね。繰り返しの部分は.*とキャプチャ\2を組み合わせて表現できます。
パターンの中に(.*)が3つありますが、それぞれ以下の文字列がキャプチャされています。

f:id:hitoridehitode:20220223111214p:plain
キャプチャされている文字列を確認

(.*)(.*)という正規表現をみると、一見「どこからどこまでキャプチャされているの?」と分からなくなります。
しかし後方に\2があることによって、正規表現のエンジンが繰り返しのパターンがあることを知ります。よって、正規表現のエンジンは(.*)(.*)\2の整合性を取るようにキャプチャします。
素晴らしいですね!

f:id:hitoridehitode:20220223124917p:plain
正規表現のエンジンは(.)(.)を「よしな」にキャプチャする。

例えば、vscodeを使っていれば正規表現の置換を使って次のように似たようなコードの生成が一気にできます。

f:id:hitoridehitode:20220223120411g:plain
vscodeでも正規表現の置換が可能

※vscodeの置換機能では、キャプチャした文字列は$nで参照するので注意してください。vscodevim経由の置換では\nの記法が利用できます。

まとめ

今回は正規表現の「.+」とキャプチャを組み合わせて繰り返しを含むパターンを作る方法を見てきました。
正規表現は入力のバリデーションやプログラムでの置換として使う以外にも、プログラミング中やsedを使ったデータの加工にも利用できます。
エディタを使う際、時々あえて正規表現を使ってみて慣れることで、ちょっとしたデータの加工にsedを使ったり、コードの生成を効率良く行うような「使い捨ての処理」を書くことができるようになります。

その積み重ねで、時間がかかっていたタスクを一瞬で片付けるための引き出しが増えていきそうですね!!

参考図書