« AppleScriptでテキストを簡単に読み込む[更新] | トップページ | AppleScriptと一行Perlについて »

2013年3月27日 (水)

AppleScriptでPerl正規表現置換

これまで筆者が Apple Script で正規表現による文字列の置換を行いたい時は、わざわざ置換用の Perl スクリプトを書いて ハードディスク内に保存し Apple Script から do shell script で呼び出すか、もしくは Apple Script を「スクリプトバンドル形式(拡張子 .scptd)にして Perl スクリプトをバンドルするか、どちらかの方法をとっていた。

だが、可能ならそのような方法ではなく、Apple Script 内だけで Perl による正規表現置換を行いたいと思い立ち、方法を模索した。

以下、当記事内では

置換処理の対象となるテキストを
「対象テキスト」(変数 obj_text)

その中で置換されるべき(置換後に消えるはずの)文字列を
「検索文字列」(search_str)

置換目的の(置換後に現れているはずの)文字列を
「置換文字列」(replace_str)

と呼ぶことにする。

(※注意:当記事の内容は OS X Mountain Lion 10.8.3 を対象にしている。他のバージョンで以下の記述が妥当なのかどうか、検証していない)

まず試したのは以下のような形である。

 set obj_text to do shell script "perl -e '$_=" & quoted form of obj_text & "; s/" & search_str & "/" & replace_str & "/sg; print;'" without altering line endings

これは完全に失敗した。実行するとエラーが出る。perl スクリプトの内容を「perl -e」の後のシングルクォーテーションマーク(')内で指定しなければならないため、「quoted form of」による参照とダブってしまうからだろうと思われる。次に

 set obj_text to do shell script "perl -e '$_=\"" & obj_text & "\"; s/" & search_str & "/" & replace_str & "/sg; print;'" without altering line endings

を試した。この場合、対象テキストの中にダブルクォーテーションマーク(")が含まれていなければ一応はうまく動作する。だが、筆者の場合、HTML ソーステキストを置換対象にすることが多い。HTML ソーステキストにはダブルクォーテーションマークが多用されている関係上、このやり方では問題がある。

次に試したのは、二行に分けるやり方である。

 set command to "$_=" & quoted form of obj_text & "; s/" & search_str & "/" & replace_str & "/sg; print;"
 set obj_text to do shell script "perl -e " & quoted form of command without altering line endings

このやり方だと、先ほどの例とは逆に、シングルクォーテーションマークマークが含まれているテキストを対象にするとエラーが出てしまう。

上の二例の失敗をふまえ、対象テキストを標準入力から Perl に渡すことにしてみた。

 set obj_text to do shell script "/bin/echo -n " & quoted form of obj_text & " | perl -e '$_=join \"\",<>; s/" & search_str & "/" & replace_str & "/sg; print;'" without altering line endings

このやり方により、ようやく対象テキスト内にどのようなクォーテーションマークマークが含まれていても正常動作が可能となった。

しかしながら、この場合でも検索文字列や置換文字列にスラッシュ(/)が含まれているとエラーが出る。なぜなら、Perl 内で置換の区切り文字としてスラッシュを使っているからである。

Perl ではスラッシュ以外の文字を区切り文字に指定することができる。例えば | に指定して「s|検索文字列|置換文字列|sg;」と書けばスラッシュによって誤作動することは避けられる。だが、そうしたところで、今度は対象テキスト内に | が含まれていればやはりエラーが出るわけだから、根本的な解決にはならない。

そこで、また二行に分けて

 set command to "$_=" & quoted form of obj_text & "; $search_str=" & quoted form of search_str & "; $replace_str=" & quoted form of replace_str & "; s/$search_str/$replace_str/sg; print;"
 set obj_text to do shell script "perl -e " & quoted form of command without altering line endings

としてみた。これだと区切り文字であるスラッシュが含まれていても正常に動作する。

しかし、この方法だと置換文字列(replace_str)の中で「$1」とか「$2」とかが使えなくなってしまう。$1 などによって検索文字列の一部を継承できることこそが、正規表現置換の最も便利な点なのに、それができないということになる。それでは意味がない。

そこまで考えたところで、ふと気づいた。

検索文字列や置換文字列を変数に入れる必要があるのだろうか(苦笑)。ハンドラー(ユーザー定義命令)の中に入れるというのであればそうする必要はあるが、実際のところ、上記の置換処理はハンドラー化しなければいけないほど長いわけでもない。

なので、実用的な意味合いから言えば、置換の都度、以下のように書けば十分と思われる。

 set obj_text to do shell script "/bin/echo -n " & quoted form of obj_text & " | perl -e '$_=join \"\",<>; s/hoge(.+?)hige/foo$1bar/g; print;'" without altering line endings

(hoge と hige で挟まれた文字列を、foo と bar で挟んだ状態に置換する、という処理)

その上で、s/〜/〜/sg; の間に / を入れてしまわないよう心がければよいだろう。

注意すべき点として、検索文字列と置換文字列を Perl の正規表現に合わせてエスケープする場合、バックスラッシュを二重に書く必要がある。例えば、括弧を除去するという処理なら

 set obj_text to do shell script "/bin/echo -n " & quoted form of obj_text & " | perl -e '$_=join \"\",<>; s/[\\(\\)]//g; print;'" without altering line endings

となる。


ハンドラー化するのであれば、

 on perl_replace(obj_text, search_str, replace_str)
   (* ここで search_str  replace_str 内のスラッシュをエスケープ *)
   set obj_text to do shell script "/bin/echo -n " & quoted form of obj_text & " | perl -e '$_=join \"\",<>; s/" & search_str & "/" & replace_str & "/sg; print;'" without altering line endings
   return obj_text
 end perl_replace

のようになる。エスケープ処理は、Apple Script の text delimiter でも行えばよいだろう(面倒臭いので書くのを省略した(笑))。

もっとも、スラッシュを自動でエスケープしたとしても、結局のところ、search_str のほうに括弧類 ( ) { } [ ] や + やピリオドを含めるなら、それらは手動でエスケープしておかねばならない。


« AppleScriptでテキストを簡単に読み込む[更新] | トップページ | AppleScriptと一行Perlについて »

コメント

コメントを書く

(ウェブ上には掲載しません)

« AppleScriptでテキストを簡単に読み込む[更新] | トップページ | AppleScriptと一行Perlについて »