PR
レシピ
OS:Windows XP
使用言語:JScript

 今回はJScript+HTA(HTML Application)で,指定されたディレクトリ内ファイルすべての指定語句を一括置換するというツールを作ります。2005年7月号の旧連載5回目でgrepを作りましたが(http://itpro.nikkeibp.co.jp/article/COLUMN/
20060120/227640/
からご覧になれます),今回はそれに置換機能を付加して,さらに検索語句に正規表現も搭載するという優れものです。


リスト1●複数のファイル内の文字列を一括置換するプログラム(JScript )
[画像のクリックで拡大表示]

図1●リスト1の実行結果
[画像のクリックで拡大表示]

 Java,Visual Basic(VB),C#系のプログラム開発では,コード内の文字の一括置換という要望はそれほど高くないと思います。昨今のIDE(統合開発環境)には,変数宣言を書き換えるとコード全体で該当変数を自動置換してくれる機能などがあるからです。ところが一つのシステムで100~300ファイルを使うことのあるWebプログラムでは,ページ内文言の統一であるとか参照includeの階層変更ということが日常茶飯事です。grepで該当語句を含むファイルを探しても,一つずつ開いて直してという作業は非効率の極みです。そこで一発置換です。

 置換や検索,そこに正規表現機能も搭載するというと,実装レベルではPerlやPHPが圧倒的に楽なんですが,今回はUIの作成が簡単で,正規表現もファイル操作も使えるという形態を考慮し,HTAで作ってみます。言語にはJScript(JavaScriptではありません)を使います。

 今回は検索語句として確定文字列だけではなく,正規表現も可能として作ってみました。例えばVBまたはvb,あるいはVbをVisualBasicに置換したいといった場合に正規表現ならば次のように書けます。

検索語句
[Vv][Bb]
置換文字列
VisualBasic

 Vまたはvの次にBまたはbの文字列は,VisualBasicに置換といった指示です。単純な検索と比べると,記述の揺らぎにも対応できるのでかなり便利です(なおこのサンプルは,置換後語句について正規表現は使えません)。

 リスト1[拡大表示]のサンプル・コードをエディタで入力してeregrep.htaのような名前で保存してください。拡張子はhtaで固定です。実行したら対象とするディレクトリ(フォルダ)を指定します(図1[拡大表示])。ディレクトリの指定は必ずドライブ名からで「C:\foo」のようにしてください。続いて検索対象となる文字を指定します。ここは正規表現が使えます。正規表現は /正規表現/ のように「/(スラッシュ)」でくくりますが,このサンプルでは不要です。また検索オプションとしてコード内で「g(グローバル・マッチ)」を指定しているので,対象ファイル内で正規表現に合致するすべての単語が置換対象となります。最初の一つだけ置換といったことはできません。


リスト1 ●複数のファイル内の文字列を一括置換するプログラム(JScript )
<html><head><title>htaで一発置換</title>
<script language="JScript">

function rep(){
 //テキスト・エリアの初期化
 form1.result.value = "";
 //検索語句またはディレクトリ名が空
 if(!form1.oldword.value || !form1.dir.value){
  alert ("ディレクトリまたは検索語句が空です");
  return;
 }

 //各種宣言
 var fso;
 fso = new ActiveXObject("Scripting.FileSystemObject");
 var strNew = document.form1.newword.value;
 var strOld = form1.oldword.value;

 var RE = new RegExp(strOld,"gm");
 var fldr = fso.GetFolder(form1.dir.value);
 var flist,x,rt,nts,ts;

 var enu = new Enumerator(fldr.files);
 for (; !enu.atEnd(); enu.moveNext()){
  x = enu.item();
  ts = fso.OpenTextFile(x, 1);
  strOrg = ts.ReadAll();
  ts.Close();
  rt = strOrg.replace(RE, strNew);
  if(rt!=strOrg){
   nts = fso.CreateTextFile(x, true);
   nts.Write (rt);
   nts.Close();
   form1.result.value += x + "\n";
  }
 }
 alert("置換完了しました");
}


</script></head>
<body ><form name="form1">
<p>検索対象は指定されたディレクトリのすべてのファイルになります。</p>
<p>ディレクトリの指定(例: C:\foo\bar)<br />
<input type="text" size="50" name="dir"></p>
<table><tr>
<td>検索語句</td>
<td>置換語句</td>
</tr><tr><td>
<input type="text" name="oldword">→</td>
<td><input type="text" name="newword"></td></tr>
</table><br />
<p><input type="button" value="置換実行" onClick="rep()"></p>
<p>置換されたファイルの一覧
<textarea name="result" cols="50" rows="20"></textarea></p>
</form></body></html>

 ディレクトリの指定と検索語句についてはコード内でNullチェックが入ります。何かが入力されていなくてはなりません。ディレクトリが存在しなかった場合には,FileSystemObject(FSO)によるエラーが表示されます。

 置換語句は,検索語句にマッチした文字列を何に置き換えるかの指定部です。置換語句のテキストボックスはNullを禁止していないので,例えばABCという文字列を削除(=Nullに置換)ということもできます。

 ファイル一覧の取得,読み込み,書き出しにはFSOを使っています。正規表現と置換はRegExpオブジェクトを使用します。使い方は簡単で,次のようにします。

var RE = new RegExp(正規表現,"オプション");

ここの正規表現部分に検索語句を入れ,今回はオプションで「gm(グローバル・マッチでマルチライン対応)」を指定しています(リスト1の(1))。

 正規表現にマッチした部分を置換するのはReplace関数です(2)。

置換された新文字列 = 
  対象文字列.replace(RE,"置換
  語句");

あとは置換前と置換後の文字列を比較して等しくなければ置換が行われたと判断できるので,ファイルを上書き保存します。

 このサンプルは対象となるファイルに対して,拡張子などでの規制がかかっていません。したがって指定されたディレクトリ内にbook1.xlsやEXE/DLLファイル,画像ファイルなどがあると,これらのファイルの中身に対しても置換が強行されます。必ずバイナリ・ファイルのないディレクトリで確認してください。

 またディレクトリの再帰をやっていないため,指定ディレクトリ以下に下位階層があっても,そこは置換対象とはなりません。下位ディレクトリまで検索して置換できたり,バイナリは対象外とする,正規表現オプションを設定できるようにするなどの改良を加えていくと,HTAでもかなりの置換ツールが完成しそうです。ぜひ挑戦してみてください。