1. 「sed」では期待した変換ができなかった

テキスト置換といえば「sed」。

ある文書のテキストを置換したいのでsedを試してみたのだけれど期待した置換を実現できなかった。

調べた結果「perl」なら実現できることが分かったので今後はテクスト置換は「perl」を使用していきますというお話です。

期待した変換から「sed」による変換の失敗、そして「perl」による置換方法を共有します。

2. 期待した変換

例えばですが下記のようなテキスト

test.html
<div class='delete'>abcde</div><div class='non delete'>12345</div>

があったとします。そして、「class='delete'」のdivタグで囲まれた範囲を削除したいとします。

期待する変換結果は

output.html
<div class='on delete'>12345</div>

です。最初のdivタグを削除します。

ここで、範囲の最後は「</div>」となりますが、この最後のタグが2つあります。そして一つ目のものを範囲の最後として処理したいのです。

ここまでの要件で過去の経験から思いつく用語があります。それは、

「greedy」 or 「no greedy」

そして、今回期待しているのは「non greedy」 : 最短一致です。

3. 「greedy」と「no greedy」

テキスト置換の基礎知識として「greedy」と「no greedy」を簡単に説明します。

  1. 「greedy」 : 最長一致

  2. 「no greedy」:最短一致

テキストマッチングでは最長一致と最短一致があります。

再度、例文を用いて説明してみます。

test.html
<div class='delete'>abcde</div><div class='on delete'>12345</div>

ここで「<div class='delete'>から</div>」の範囲にマッチングを試してみます。

3.1. 「greedy」 : 最長一致

できるだけ長い文章がマッチングするように処理します。マッチングするテキストは

output.html
<div class='delete'>abcde</div><div class='on delete'>12345</div>

全文となります。

3.2. 「non greedy」 : 最短一致

できるだけ短い文章がマッチングするように処理します。マッチングするテキストは

output.html
<div class='delete'abcde</div>

と最初のdivタグに囲まれた範囲となります。

4. 「sed」を試してみるが、最短一致の機能がないらしい

Terminal
sed -e "s|<div class='delete'>.*</div>||g" test.html

上記では最長一致するのですべてのテキストがマッチングして削除されてしまいます。

そこで最短一致の機能を調べて見ましたが「sed」にはないようです。

ニッチな質問なのか情報が見つかりにくいのですが見つけた質問サイトが下記となります。

sedではなくperlを使用するように回答されています。

5. 「perl」を試してみて期待する結果が得られた

下記のようにperlを使用することで期待する結果を得ることができました。

Terminal
perl -pe "s|<div class='delete'>.*?</div>||g" test.html
output.html
<div class='non delete'>11345</div>

6. まとめ

テキスト置換は「sed」という思い込みがあったのですが、今後は「perl」をメインで使用していきます。

今回は以上です。