今回は、で記事のマークダウンファイルや HTML 内の特定の内容を探し出したり書き換えたりするのに便利な
findRE
とreplaceRE
についてご紹介したいと思います。
正規表現を使って探索・置換するための関数
この2つの関数の語尾についている RE というのは Regular Expression(正規表現)のことです。
正規表現とは、特定のパターンを満たす文字列を表現する方法です。
例えば、特定のアルファベット一文字の後ろに2桁までの数字が続くようなパターン(h2
, u11
)とか、
特定の文字列で挟まれたパターン(<div>foo</div>
)とかを探したいときに使用します。
findRE の使い方
指定された範囲の中で、指定された正規表現にマッチする部分のリストを返します。
次のように書きます。
findRE (正規表現のパターン) (入力) [(最初から何番目までを返すか)]
例
例えば、
{{ findRE `a(.*?)e` `abcde apple je m'appelle apfel` }}-> [abcde,apple,appe,apfe]
となります。
また、$input
の中身が次のものだったとして、
1<div>2<h2 class="omidashi">大見出し1</h2>3<p>いろはにほへと</p>4<p>ちりぬるを</p>5<h3 class="komidashi" id="#小見出し1">小見出し1</h3>6<p>わかよたれそ</p>7<p>つねならむ</p>8<h2 class="omidashi">大見出し2</h2>9<h3 class="komidashi" id="#小見出し2">小見出し2</h3>10<p>ういのおくやま</p>11<p>けふこえて</p>12<h3 class="komidashi" id="#小見出し3">小見出し3</h3>13<p>あさきゆめみし</p>14<p>えひもせす</p>15</div>
この中からh3
タグで挟まれている部分だけ取り出したいときは次のようになります。
{{ findRE `<h3.*?>(.|\n)*?</h3>` $input }}-> [<h3 class="komidashi" id="#小見出し1">小見出し1</h3>,<h3 class="komidashi" id="#小見出し2">小見出し2</h3>,<h3 class="komidashi" id="#小見出し3">小見出し3</h3>]
h3
タグ直後のp
タグを取り出したい場合は次のようになります。
{{ findRE `</h3>( |\n)*?<p>(.|\n)*?</p>` $input }}-> [</h3><p>わかよたれそ</p>,</h3><p>ういのおくやま</p>,</h3><p>あさきゆめみし</p>]
また、第三引数を指定することで、その個数分だけ取り出します。
{{ findRE `</h3>( |\n)*?<p>(.|\n)*?</p>` $input 1 }}-> [</h3><p>わかよたれそ</p>]
注意点
返ってくる値はリストなので、ひとつひとつの要素を扱いたい場合にはindex
かrange
を使うことになります。
正規表現のマッチがひとつしかない場合も
index (findRE ...) 0
とする必要があります。
ただし、存在するかしないかで条件分岐するだけなら、空のリストはfalse
となるため、
if findRE ...
とそのまま書いても判定できます。
replaceRE の使い方
指定された文字列の、指定された正規表現のパターンに該当する部分を、指定されたものに置き換えたあとのものを返します。
次のように書きます。
replaceRE (正規表現のパターン) (置換先) (入力)または(入力) | replaceRE (正規表現のパターン) (置換先)
置換先に$1
のように書くと、正規表現でマッチしたグループを表現できます。
$1
なら1グループ目、$2
なら2グループ目、$0
ならマッチした部分全体です。
例
例えば、
{{ replaceRE `a(.*?)e` `a-e` `abcde apple je m'appelle apfel` }}-> a-e a-e je m'a-elle a-el{{ replaceRE `a(.*?)e` `$1` `abcde apple je m'appelle apfel` }}-> bcd ppl je m'pplle pfl
となります。
また、$input
の中身が次のものだったとすれば、
1<div>2<h2 class="omidashi">大見出し1</h2>3<p>いろはにほへと</p>4<p>ちりぬるを</p>5<h3>小見出し1</h3>6<p>わかよたれそ</p>7<p>つねならむ</p>8<h2 class="omidashi">大見出し2</h2>9<h3>小見出し2</h3>10<p>ういのおくやま</p>11<p>けふこえて</p>12<h3>小見出し3</h3>13<p>あさきゆめみし</p>14<p>えひもせす</p>15</div>
1{{ replaceRE `<h3>(.*?)</h3>` `<h3 class="komidashi" id="#$1">$1</h3>` $input }}23->4<div>5<h2 class="omidashi">大見出し1</h2>6<p>いろはにほへと</p>7<p>ちりぬるを</p>8<h3 class="komidashi" id="#小見出し1">小見出し1</h3>9<p>わかよたれそ</p>10<p>つねならむ</p>11<h2 class="omidashi">大見出し2</h2>12<h3 class="komidashi" id="#小見出し2">小見出し2</h3>13<p>ういのおくやま</p>14<p>けふこえて</p>15<h3 class="komidashi" id="#小見出し3">小見出し3</h3>16<p>あさきゆめみし</p>17<p>えひもせす</p>18</div>
となります。
注意点
正規表現のパターンに該当する部分が置き換わるだけであり、
入力文全体が置き換わるわけではありません。
例えばさきほどのfindRE
の$input
の例で、
h3
タグ直後のp
タグの内容を取得しようとして、
{{ $pattern := `</h3>(?: |\n)*?<p>((?:.|\n)*?)</p>` }}{{ $find := findRE $pattern $input }}{{ range $find }}{{ . | replaceRE $pattern `$1` }}{{ end }}
とすれば
わかよたれそういのおくやまあさきゆめみし
と、うまく取得できます。
しかし、
1{{ $pattern := `</h3>(?: |\n)*?<p>((?:.|\n)*?)</p>` }}2{{ $find := findRE $pattern $input }}3{{ range $find }}4{{ . | replaceRE `<p>((?:.|\n)*?)</p>` `$1` }}5{{ end }}
とすれば
</h3>わかよたれそ</h3>ういのおくやま</h3>あさきゆめみし
というふうに、</h3>
を残してしまいます。
割とはまりがちな上に気づきにくいので気をつけたいところです…。
なお、正規表現を使う必要がないなら、replace
を使うこともできます。
Hugo のテンプレートを使って書き換えを行う時には必ずと言っていいほど頻繁に使う関数なので、
ぜひ覚えておいてください。
この記事がお役に立てたならうれしいです。
それでは