製品版サンプルVZ.DEFの初期設定で^L/[F05]にアサインされている「検索文字列の取得」は,コマンドがありながら同じキーでマクロの「検索文字列の連続取得」が呼び出される仕様になっています。検索文字列の取得そのものを行うのはコマンド#G
(#59
)で,非ブロックモードではカーソル位置以降の1語,ブロックモードでは改行を含まないブロック範囲を取得します。
マクロ中で文字列を取得する場合は,結局はブロック範囲をどのように指定するのかというマクロと人の意図とのインターフェイスの問題に集約されます。「自動」の背後にあるのは,面倒な状況分析と膨大なエラー処理なのですが,個々のケースによって異なるものなので,ここでは典型的な例にとどめておきます。
次の例は,カーソルの位置する文字を含む語(同じ文字タイプをもつ文字列)を取得するサンプルです。
1 ^\ "" ?.
(ct<3)?. ;空白文字は取得しない
(mb==2)?>g mb-, ;文字ブロックのみそのまま取得
t=ct, ;カーソル位置の文字タイプを保存
(-1){ ;ループの開始
#s ;左へ移動
(ct!=t || r)?{} ;タイプが違うかテキスト先頭なら
} ;ループを抜ける
(r+1)?#d ;取得位置先頭へ
#b ;ブロックモードに入る
(-1){ ;ループの開始
#d ;右へ移動
(ct!=t)?{} ;文字タイプが異なればループを抜ける
}
:g #G ;文字ブロックを検索文字列として取得
VZにおける検索というのは,検索文字列の設定と実際の検索が別になっていて,他のアプリケーションにない独特の趣があるわけですが,設定後すぐに自動検索してくれれば手間が減ることも確かです。そこで自動的に検索を開始するマクロを作ってしまえばよいということになります。次のマクロは,自動的に前方検索を行って,見つからなければ後方検索を行うサンプルです。
1 ^\ "" ?.
#F &Cr (r)?. ;検索文字列設定:キャンセルなら終了
#c ;まず前方検索
(r)?{ #r &b(2) } ;後方検索に移った時はビープを鳴らす
(r)?{ &m(19) . } ;それでも見つからなければ終了
&m("%c",cm+'∞') ;検索方向を↑↓で表示
マクロの中では文字列を二重引用符で括るということになっています。ところが引用符を含む文字列を扱おうとしたときに,そのままでは引用符の入れ子がくずれてしまいます。
1 ^^ "" ?. h2, a1, b1,
&?("<h%d id="faq%02d_%03d">",h,a,b) ;エラー発生でマクロ停止
この例の場合では,「<h%d id=」が出力すべき文字列とされ,閉じる引用符の直後に引数も閉じ括弧もないため,マクロとして解釈できないコードとなってしまい,実行が停止してしまいます。
このような引用符を含む文字列を扱う場合は,引用符の意味を打ち消さなくてはなりません。VZマクロでのメタ文字をエスケープするメタ文字は'$'ですので,内側の二重引用符は'$'を前置します。
&?("<h%d id=$"faq%02d_%03d$">",h,a,b) ;所期の動作
さて前項で出てきたメタ文字をエスケープするための'$'ですが,これもメタ文字です。さて,困ったことに,'$'は正規表現においては行末を意味するメタ文字ですから,文字列の末尾に出現することが圧倒的に多いわけです。したがって,次のようなケースでは文字列終端の閉じ二重引用符がエスケープされてしまって,これまたエラーになってしまいます。
#F &?("\x ^<h%d[^>]*>.+</h%d>$",h,h) #m
メタ文字'$'の意味を打ち消すのもメタ文字'$'を使用しますので,以下のように'$'をかさねてやります。
#F &?("\x ^<h%d[^>]*>.+</h%d>$$",h,h) #m
マクロの中での一連の作業でテキスト中の文字列を検索するというケースがあると思います。ただし,普通にコマンド#F
(検索文字列の設定)を使用すると,検索文字列バッファに意図しない文字列が蓄積されてしまって,手動検索時に履歴の呼出が煩わしいという問題が発生します。
この問題を解決するためには,二通りの方法があります。
そのマクロで検索する文字列が固定している場合は,マクロバッファに読み込まれている文字列の場所を検索文字列のバッファとみなして検索させることが可能です。次の例のように,直接指定してください。
1 ^^ "" ?.
mp[=2, ;検索モードに変更
ps[="hoge", ;psをプッシュしつつ直接指定する
#c a=r, ;検索する(成功/失敗を保存)
ps], ;psを元に戻す
mp], #] ;ページングモードを戻してステータス再描画
psのプッシュ/ポップが非常に重要で,ポップを忘れたり,ポップする前にマクロが停止したりすると,psの指すアドレスが元に戻らないことが原因で最悪の場合ハングアップします。何故そのような重大な不具合が発生するかというと,元のpsがヒストリーバッファの構造を持っているのに対し,ただの文字列ではサイズ情報がでたらめなため,際限なく検索文字列を格納しようとしてマクロバッファを壊してしまうからです。この例で言えば,サイズ情報が'="'(223Dh)つまり8765バイトもの大きさのヒストリーバッファということになってしまって,この大きさの単位でマクロバッファが壊れていくわけです。
書式出力などを利用して検索文字列を設定する場合には,バッファのすりかえという手法を用いて検索文字列バッファを汚さないようにできます。すりかえるバッファはヒストリーバッファでそれなりのサイズがあれば,問題なく使用できます。通常は,prやpaといったバッファで代用するのが一般的で,いずれのバッファも汚したくない場合は自前ヒストリーバッファを用意します。
1 ^^ "" ?.
a=1, b=2, ;値の例
mp[, ps[=pa, ;ページングモード,psの保存・すり替え
#F &?("%05u/%05u",a,b) #m ;検索文字列設定
#c ;前方検索
ps], mp], #] ;ps等の復帰
1 ^^ "" ?.
a=1, b=2, ;値の例
mp[, ;ページングモード保存
&> ps[=r, ;psの保存・自前バッファへのすり替え
#F &?("%05u/%05u",a,b) #m ;検索文字列設定
#c ;前方検索
ps], mp], #] ;ps等の復帰
0: (5+$)."$(50,0)$[82]"
テキストの余分な空行を削除するためには,必要な空行はどのくらいかを決めておかなくてはなりません。ここでは仮に,3行以上連続する空行があれば,2行空きに整えるものとします。
空行のある部分の前後を図にすると,次のようなイメージになります。
○○○○○[改行] [改行] [改行] [改行] ○○○○○[改行]
つまり,3行空きになっている場合,直前の文字のある行の改行を含め4個の連続した改行があることがわかります。この部分に関して言えば,4個の連続する改行を3個にしてやることで2行空きに整えられますね。これは置換で行うのが手っ取り早いわけで,次のようなコードになります。
#56 "\n\n\n\n" #m "\n\n\n" #m "ay"
さて,この置換結果ですが,5個の連続した改行があった場合は4個に整理されます。結局,一度の置換では置換しきれないということになります。このような場合に使えるのがコマンド#57
(直前の置換の再実行)で,同じ置換を繰り返しつつコマンドの返り値としてrに格納される置換数が0になるのを待つという手順になります。
#56 "\n\n\n\n" #m "\n\n\n" #m "ay"
(-1){ #57 (r)??{} } ;置換数が0になるまで繰り返し
すべての空行を削除する場合は,次の通り。同じことですね。
#56 "\n\n" #m "\n" #m "ay"
(-1){ #57 (r)??{} } ;置換数が0になるまで繰り返し