Aファイルから同時に開いているBファイルへの書き込みと、Aファイルへの復帰マクロ

  1. フォーラムの皆様、いつもお世話になっております。

    長文のAというファイルを編集しており、その中から索引の対象としたい語句を選択し、改行文字を加えてからBファイルへ書き込みたいと考えております。

    選択範囲を新しいファイルに書き込む方法は、ライブラリにある、手石様作成の「ファイル(選択範囲)の仮保存」マクロ内の、

    var s = Editor.ActiveDocument.Selection.Text;
    if ( s.length == 0 ) { Quit() ;}
    var filename = document.FullName;
    Editor.NewFile();
    var newdoc = Editor.Documents.Item( Editor.Documents.Count-1 );
    newdoc.Write( s );
    newdoc.save( filename + tail() );
    newdoc.saved = true;
    newdoc.close();
    後略

    というコードが参考になったのですが、Bファイルを閉じずに開いたまま、Aファイルの選択文字列をマクロを実行するたびにBファイルに書き込んでゆくにはどうすれば良いでしょうか?

    マクロからBファイルを開くことはできたのですが、そこへ書き込むことができません。

    var targetFile = Editor.Documents.Item( ?????? );
    targetFile.Write( selectedWord );

    のように、何とかしてBファイルの Item プロパティを取得し、変数に入れれば良いのかな?と思うのですが、具体的なコードがわかりません。
    Bファイルをアクティブ? にする必要があるのでしょうか?

    実を言うと、Editor.Documents.Count-1 と指定することでなぜ新規に開いたファイルが選択できるのかも理解できない状態です。

    Aファイル中には拾いたい語句が多数あるので、マクロ終了時にはAファイルに戻れると嬉しいです。

    あまりにも基本的なことがわかっていないので恐縮ですが、
    ご指導いただければ幸いです。よろしくおねがいします。

     |  Takeshi  |  返信
  2. フォーラムの皆様、いつもお世話になっております。

    正解かどうかわかりませんが、一応目的の動作はできるようになりました。

    > 実を言うと、Editor.Documents.Count-1 と指定することでなぜ新規に開いたファイルが選択できるのかも理解できない状態です。

    下記のコード、

    alert(" 文書の数は " + Editor.Documents.Count);
    for (var i = 1; i <= Editor.Documents.Count; i++) {
    	alert( "i = " + i + "ファイル名は : " + Editor.Documents.Item(i).Name);
    	Editor.Documents.Item(i).Write( s + "\n" );
    }

    を試したら、文書の数は 2 、i = 1 の時にBファイル名が表示されたので、そこへ書き込みました。しかし、i = 2 となるとエラーが発生します。

    じゃあ決め打ちで行けば良いのかと、

    var s = Editor.ActiveDocument.Selection.Text;
    if ( s.length == 0 ) { 
    	Alert("何も選択されていませんので終了します!");
    	Quit() ;
    }
    
    Editor.Documents.Item(Editor.Documents.Count-1).Write( s + "\n" );

    というコードにしてみました。Aファイルはアクティブのまま、バックグラウンドでBファイルに書き込まれます。

    ファイルを3つ開いた場合はどうなるかわかりませんが、自分用にはこれで良いかなと妥協します。

    お騒がせいたしました。もし何かコメントいだだければ幸いです。

     |  Takeshi  |  返信
  3. フォーラムの皆様、いつもお世話になっております。自己レスです。

    エラーの原因は、Documents コレクションのCount プロパティで取得した編集中のファイル数が 3 であった場合、Item プロパティで指定するインデックスがゼロから始まって、0,1,2 となることを知らなかったからでした。
    (おそらくそうだと思います.....)

    下記のようにコードを書き直したら、多数のファイルを編集していても、それらから指定したBファイルへと書き込めるようになりました。

    // 選択文字列を指定ファイルに書き出すマクロ
    
    const targetFileName = "B.txt"; // 書き込み対象のファイル名を定数として指定
    
    var selectedWord = Editor.ActiveDocument.Selection.Text;
    if ( selectedWord.length == 0 ) { 
    	Alert("何も選択されていませんので終了します!");
    	Quit() ;
    }
    
    var docColl = Editor.Documents;
    for (var i = 0; i < docColl.Count; i++) {
    	var eachDoc = docColl.Item(i);
    	if (eachDoc.Name == targetFileName) {
    		eachDoc.Write( selectedWord + "\n" );
    		eachDoc.Save();
    	}
    }

    お騒がせいたしました。

     |  Takeshi  |  返信
  4. ファイルAで実行したマクロからすでに開いているファイルBを指定してアクティブ化その他の操作するのは、あまり簡単な処理ではないんですよね。 :(
    すでに解決したようですが、昨晩は文章を起こしてる途中で寝落ちしてしまい...、 一応、書き上げたので投稿しておきます。

    > 実を言うと、Editor.Documents.Count-1 と指定することでなぜ新規に開いたファイルが選択できるのかも理解できない状態です。
    もうすでに分かっていることとおもいますが、Editors.Item(n) は Editor オブジェクト、Editors.Item(n).Documents.Item(m) は Document オブジェクト扱いになります。
    これもすでにお気づきになったように、Editors や Documents のプロパティについては「Count は 1 からの整数値、Item は 0 からの整数値」となっています。 Count は配列の length プロパティとおなじ、Item は配列の index プロパティとおなじ扱いができ、配列の操作とおなじ書き方の for 文や while 文などで目的のファイル名 Name/FullName に一致するものを探したりもできます(これもすでに遅しですが、拙作「すべて閉じる(無題は保存の確認しない)」マクロがサンプルコードになったかも)。
    Editor.Documents.Count-1 については、参考にしたというマクロでは Editor.NewFile(); で無題ドキュメントを「さいごのタブ」として新規に開いているので Editor.Documents.Item( Editor.Documents.Count-1 ) となっていたわけです。

    つねに無題ドキュメントや特定のファイル(ファイルB)を「新規タブで開いてから」操作するという前提なら Editor.Documents.Item( Editor.Documents.Count-1 ) ですが、実際にはトピックの表題のようにファイルBがすでにどこかのタブで開いている状況を考慮する必要もありますよね。
    それを解決してくれるコードとして、 kazy さんの「Editor.OpenFile()のポリフィル」があります(上述のごとく Count や Item を length や index と同じように扱って、入れ子になった配列をチェックするのと同じようなコードになっています)。
    https://www.haijin-boys.com/wiki/Editor.OpenFile()のポリフィル
    まあ、自分専用のマクロならそれほど苦労せずに済む話なんですが、マクロを公開用にする場合は TDI (タブ有効)だけでなく SDI スタイル(タブ無効)での複数ウインドウ(Editors.Count/Item)も考慮したほうがいいということなんでしょう(とはいっても、SDI を切り捨てているマクロのほうが多いですが)。 :)

    長々と書きましたが、実際のところ「Editor.OpenFile()のポリフィル」の関数 function OpenFile( file, encoding, flags ) を導入すれば、すでに開いているファイルでも関数が for 文の処理をしてファイルBをオブジェクトとして返してくれます。
    ※「Editor.OpenFile()のポリフィル」の引数については
      ref. https://www.haijin-boys.com/wiki/マクロリファレンス:Editor_インターフェイス#OpenFile_メソッド

    あと、実運用では、マクロからファイルBを編集するときに書き込み箇所を文書末の行頭にするなど位置を調整する必要があるかと。

    // サンプルコード
    var selectedWord = document.selection.Text.replace( /\n$/, "" );
    if ( ! selectedWord.length ) {
      Quit();
    }
    Redraw = false;
    
    // すでにファイルBを開いているとしても関数 OpenFile() にパスで指定する
    var docuB = OpenFile( "c:\\hoge\\ファイルB.txt", 0, 1 );
    
    docuB.selection.EndOfDocument();
    // 書き込み位置調整 / Writeln() を1回にして undo しやすくする
    if ( docuB.selection.GetActivePointX( mePosLogical ) > 1 ) {
      selectedWord = "\n" + selectedWord;
    }
    docuB.Writeln( selectedWord );	// Write( selectedWord + "\n" ) とおなじ意
    
    // docuB.Save();		// 必要なら
    // docuB.Close();	// 必要なら
    document.Activate();	// 元のドキュメントに戻る
    Redraw = true;
    
    // ---------- ▼ 関数を貼り付ける ▼ ----------
    // 「Editor.OpenFile()のポリフィル」
    function OpenFile( file, encoding, flags ) {
      // 【略】
    }

    さいごに余計なことかもですが、{ Alert("ほげ"); Quit(); } はデバッグ作業用として、公開用マクロの場合には { Status = "ほげ"; Quit(); } のほうがよいとおもいます、「音」的な意味で。 :)

     |  sukemaru  |  返信
  5. sukemaru様 アドバイスまことにありがとうございました。お忙しいところ、2日がかりで解説、コーディングしていただき、感謝感激です。

    > ファイルAで実行したマクロからすでに開いているファイルBを指定してアクティブ化その他の操作するのは、あまり簡単な処理ではないんですよね。 :(

    そうでしたか。無知は怖いですね。(笑)

    >(これもすでに遅しですが、拙作「すべて閉じる(無題は保存の確認しない)」マクロがサンプルコードになったかも)。

    こちらのマクロもこれから解読させていただきます。

    > それを解決してくれるコードとして、 kazy さんの「Editor.OpenFile()のポリフィル」があります(上述のごとく Count や Item を length や index と同じように扱って、入れ子になった配列をチェックするのと同じようなコードになっています)。

    これも解読したいと思います。

    > まあ、自分専用のマクロならそれほど苦労せずに済む話なんですが、マクロを公開用にする場合は TDI (タブ有効)だけでなく SDI スタイル(タブ無効)での複数ウインドウ(Editors.Count/Item)も考慮したほうがいいということなんでしょう(とはいっても、SDI を切り捨てているマクロのほうが多いですが)。 :)

    実は、最初のうちはタブ無効で試していたらエラー出まくりで困り果てておりました。

    > あと、実運用では、マクロからファイルBを編集するときに書き込み箇所を文書末の行頭にするなど位置を調整する必要があるかと。

    なるほど! 「動けば良い」レベルのプログラミングとの違いに感心しております。

    系統立てて学習せずに、ネット上のコードをあちこちから拾ってコピペし、エラーが出る度に試行錯誤で修正する、という方式なのでなかなか実力が付きません。精進いたします。
    重ねて御礼申し上げます。

     |  Takeshi  |  返信
スポンサーリンク