ActiveX Script Host機能を使って階層付きテキストをArrangeNoteのメモグループ-メモ構造にインポートする。使用する階層付きテキストは次のようなものである。
memo0 .title1 ..title1-1 memo1-1 ..title1-2 memo1-2 .title2 ..title2-1 memo2-1 ..title2-2 memo2-2 ..title2-3 ...title2-3-1 memo2-3-1 .title3 ..title3-1 memo3-1 .title4 memo4
階層付きテキスト読み込みスクリプトの仕様は、
この仕様に基づくPerlスクリプトは次のようである。
$fileName = $ANScript->ChooseFile( "Please Choose a textfile.", false ); open(TEXT, $fileName); $note = $ANScript->GetNote(); $layer[0] = $note->GetRootItem(); $num = 0; @memolines = (); @titles = (); while(<TEXT>){ if(/^(\.+)([^\.].*)$/){ $num = length($1); push(@titles, $num."\t".$2); }else{ if(@titles != ()){ if(@memolines != ()){ $item->{Body} = join("", @memolines); @memolines = (); } for($i=0;$i<=$#titles;$i++){ ($num, $title) = split(/\t/, $titles[$i]); if($i == $#titles){ if($num > $prenum && scalar(@titles) == 1){ $layer[$num-1] = $layer[$num-2]->NewItem( $note->{MEMOGROUP}, $note->{CHILD} ); $layer[$num-1]->{Title} = "*"; $layer[$num-1]->{Open} = true; } $item = $layer[$num-1]->NewItem( $note->{MEMO}, $note->{CHILD} ); $item->{Title} = $title; }else{ $layer[$num] = $layer[$num-1]->NewItem( $note->{MEMOGROUP}, $note->{CHILD} ); $layer[$num]->{Title} = $title; $layer[$num]->{Open} = true; } } @titles = ();$prenum = $num; }else{ if($num == 0){ $item = $layer[$num]->NewItem( $note->{MEMO}, $note->{CHILD} ); $title = $_;chomp($title); $item->{Title} = $title; $num = 1; } } push(@memolines, $_); } } close(TEXT); $item->{Body} = join("", @memolines);
2ch「アウトラインプロセッサ」のメッセージ220番にある階層付きテキスト例を読み込んだArrangeNote
IUNKNOWN BBSにはArrangeNoteの作者、山本幸一さんのPython版のスクリプトがある。このBBSでArrangeNote関連のディスカッションが可能である。ほぼ毎日書き込み有り。
通常、メールの送受信はメーラーを使うので、わざわざスクリプトを使う場面は少ないかもしれないが、定期的に特定のテキスト情報を自動収集・加工して、メールで連絡するような場合にはスクリプトで一貫して記述できると便利である。httpやftpはこれまで使ってきたけど、Windows環境のPerlでメールを送る場合はどうなんだろうと「Perl Cookbook」を開くと、WindowsやMacではsmtpサーバーを探す必要があると書いてあるだけ。UnixならSENDMAILなどのメールプログラムとの連携が簡単だから様様な選択肢があるのだろう。結局、PPMを起動して、search Mailしてみた。
PPM> search Mail Packages available from http://ppm.ActiveState.com/cgibin/PPM/ppmserver.pl?urn:/ PPMServer: Mail-Alias [1.12 ] Maniulates mail alias files of various formats. Works on Mail-Bulkmail [2.05 ] Platform independent mailing list module Mail-Field-Received [0.23 ] mostly RFC822-compliant parser of Received Mail-Freshmeat [0.94 ] class for parsing e-mail newsletters from Mail-IMAPClient [2.0.9 ] An IMAP Client API Mail-POP3Client [2.6 ] Perl 5 module to talk to a POP3 (RFC1081) server Mail-Procmail [1.01 ] Procmail-like facility for creating easy mail filters. Mail-RBL [1.00 ] Perl extension to access RBL-style host verification Mail-Sender [0.7.06] module for sending mails with attachments through an SMTP Mail-Sendmail [0.78 ] Simple platform independent mailer Mail-TieFolder [0.03 ] Tied hash interface for mail folders Mail-Vmailmgr [0.4 ] A Perl module to use Vmailmgr daemon. Mail-XML [0.02 ] Adds a toXML() method to Mail::Internet. MailQuoteWrap [0.01 ] Provides quotification functionality for Usenet MailTools [1.15 ] Parse mail addresses PostScript-MailLabels [2.02 ] Read and select from an ASCII address book as defined
Mail-Senderもあるが、取りあえずシンプルでよいだろうと、Mail-Sendmailパッケージをインストール。
PPM> install Mail-Sendmail Install package 'Mail-Sendmail?' (y/N): y Installing package 'Mail-Sendmail'... Bytes transferred: 10925 Installing J:\Perl\html\site\lib\Mail\Sendmail.html Installing J:\Perl\site\lib\Mail\Sendmail.pm Writing J:\Perl\site\lib\auto\Mail\Sendmail\.packlistドキュメントを見ると使い方は簡単だ。
use Mail::Sendmail; %mail = ( smtp => 'your.smtp.server', To => 'yourfriend@mail.address', From => 'your@mail.address', Subject => 'ipconfig ouput', Message => ($ipconfig = `ipconfig`) ); sendmail(%mail) or die $Mail::Sendmail::error; print "OK. Log says:\n", $Mail::Sendmail::log;のようなスクリプトで、ipconfigの出力をメールで自動的に送付する。あまりに簡単すぎて拍子抜けする。本を調べはじめて使うまで10分程度の話だ。便利な時代になったことを感謝する。
ActiveX Script HostとしてWindowsが標準で提供しているのは次の三つのHostである。
ご存知のように標準のスクリプト言語としてVBScriptとJScriptがあるが、主要なスクリプト言語はActiveX Script Engineを持つようになった。代表的なものはPerlであり、Python、Rubyである。スクリプタがこれを利用しない手はない。またアプリケーションとしてWSHを使ってもそれほどおもしろいことができるわけでない。ActiveX Scripting Host機能を搭載したアプリケーションとしてArrangeNoteに注目してみよう。ArrangeNoteスクリプトチュートリアル(中級編)のPerlScriptを紹介して、それをRubyやPythonに移植してみる。
$pattern = $ANScript->Input( 'Enter regular expression.', '' ); $replacement = $ANScript->Input( 'Enter replacement', '' ); $note = $ANScript->GetNote(); for( $i = 0; $i < $note->GetSelectedItemCount(); $i++ ) { $note->GetSelectedItem( $i )->{Body} =~ s/$pattern/$replacement/g; } このスクリプトをファイルに保存したら、ArrangeNoteの上でメモをいくつか 選択し、このスクリプトを実行してください。まず、「Enter regular expression.」と聞いてきますから、「\t」と入力してみてください。次に、 「Enter replacement.」と聞いてきますから、「\t」(半角空白文字4つ)を入力 してください。すると、メモの本文にあるタブ文字が半角空白文字4つに全て置 換されます。 それでは、このスクリプトについて説明しましょう。まず、1行目で「 $ANScript->Input」を使って正規表現のパターンを入力するよう促します。第1 引数はメッセージで、第2引数は入力域に最初に表示する初期値です。2行目も同 様で、置換文字列を入力するように促します。 3行目では、「$ANScript->GetNote」を使ってノートオブジェクトを取得しま す。ノートオブジェクトは現在編集しているノートの内容に直接アクセスできる オブジェクトです。 4〜6行目では、「$note->GetSelectedItemCount」と「$note->GetSelectedItem」 を使ってメモ一覧上で選択されているメモを繰り返し取得します。5行目では選 択されているメモ($note->GetSelectedItem($i)によって取得されます)のBodyプ ロパティの内容に対して「s///」を使って置換します。「g」オプションが付い ていますから、$patternにマッチする全ての文字列が$replacementに置き換えら れます。
これをActiveScriptRubyで書き直すと、
pattern = ANScript.Input( 'Enter regular expression.', '' ) replacement = ANScript.Input( 'Enter replacement', '' ) note = ANScript.GetNote() (0...note.GetSelectedItemCount).each do |i| note.GetSelectedItem( i ).Body = note.GetSelectedItem( i ).Body.gsub(/#{pattern}/, replacement) endここで、
note.GetSelectedItem( i ).Body = note.GetSelectedItem( i ).Body.gsub(/#{pattern}/, replacement)の行は、
note.GetSelectedItem( i ).Body.gsub!(/#{pattern}/, replacement)とgsub!を使うといいように考えられるが、置換が起こらない。ActiveScriptpRubyの作者のartonさんから次のようなメールをいただいた(多少編集してある)。
推測ですが、結論から言うと、痛いとこを突いてしまわれたようです。 Bodyというのが、文字列を返すプロパティだとすると、 note.GetSelectedItem( i ).Body.gsub!(/#{pattern}/, replacement) という式は、擬似Rubyで表記すると、 item = note.GetSelectedItem(i) s = item.Body s.gsub!(/#{pattern}/, replacement) と展開されます。つまり、文字列sをBodyに戻すための処理が存在しません。も し、Bodyが文字列を返すプロパティだとすると、現実問題としてCOMのオブジェ クトのプロパティの実装というのは、Rubyのアクセサ class Foo @variable attr_accessor :variable #@variableの参照 end と異なり、無理矢理Rubyで記述すると class Item @body def get_body @body.clone #参照ではなく、クローンが返される end def put_body(newstr) @body = newstr end end となっているからです。したがって、gsub!で破壊しているのは、上の擬似Ruby コードでのテンポラリな変数sで表されている文字列であって、Bodyの実体とは 異なります。したがって、 note.GetSelectedItem( i ).Body = note.GetSelectedItem( i ).Body.gsub!(/#{pattern}/, replacement) というように使用してください。 純Rubyであれば、!でBodyが変更されないのは正常とは言えませんが、Win32OLE のオブジェクトとしては正常です。以上のような動作が意図と異なるケースもあるのでスクリプトを書く場合に多少の注意が必要である。
次はPythonで書いた例である。
import re pattern = ANScript.Input( 'Enter regular expression.', '' ) replacement = ANScript.Input( 'Enter replacement', '' ) note = ANScript.GetNote() for i in range(note.GetSelectedItemCount()): note.GetSelectedItem( i ).Body = re.sub(pattern, replacement, note.GetSelectedItem( i ).Body)ActiveX Scripting HostとしてのArrangeNoteは大変興味深いもので、Ruby等の高機能なスクリプトによってArrangeNoteの機能を大きく拡張できる可能性を秘めている。現時点では、PerlScriptはSJISをスクリプトに使うことができない。ActiveScriptRubyはSJISの正規表現パターンマッチ等、そのままでSJIS日本語処理が可能である。PythonもSJISが通るので有力な候補である(PyJUGの「日本語環境でIdleを使う」という記事を参照してSJIS対応Pythonをインストール・設定する必要がある)。日本語環境における快適なActiveX Scriptingへの最短の道はActiveScriptRubyかPythonということになる。
本記事を書くに当たって、山本幸一さんから掲示板のチュートリアルの編集掲載許可、artonさんからは問い合わせに対して丁寧なメールとメール内容の引用許可をいただいた。深謝する。
たむらさんに開設していただいたメーリングリストに現時点で30名近くの方にご参加いただいている。大変ありがたい。テキスト&スクリプトにインターネット時代の新しい方向性が打ち出せればと思う。Webはテキストでできているようなものだし(^^)
やれやれ、月が変わっちゃったね。「光陰矢のごとし」か・・・
「脱物質化社会」、ダイアン・コイル著、室田康弘+矢野裕子+伊藤恵子訳、東洋経済新報社、2001年10月18日発行、337ページ、2800円。オリジナル・タイトルは「The Weightless World」、Diane Coyle、1997年発行だ。原著が古いので買うのを止めようかと思ったが、今年1月に書かれた日本語版への序文が良かったので買うことにした。なぜWeightlessかというと、ここ100年で生活は20倍豊かになったが、生産しているものの総重量はほとんど変わらないからだ。しばらく前に流行った言葉でいうと、軽薄短小であったり、サービスに価値が移行していることを意味する。これは大きな変化で人の生き方も大きく変わる必要がある。経済的価値が、創造性、他人へのいたわり、知性など人々の資質にますます依存するようになる。最近の不況の意味を考えるにも指針となる一冊かもしれない。
「入門Perl DBI」、Alligator Descartes、Tim Bunce著、田中 幸訳、オライリー・ジャパン、2001年8月29日初版第1刷発行、377ページ、3800円。
「dRubyによる分散オブジェクトプログラミング」、関 将俊著、アスキー、2001年11月1日初版発行、174ページ、2400円。