クリップボード内容を配列で使うNo.10119
伯林 さん 23/04/07 22:49
 
初心者です。よろしくお願いいたします。

E:\○○○○\△\□□\20230402鹿児_平均使用率 買い替え.txt
E:\○○○○\△\□□\20230403兵庫_故障率 買い替え.txt
E:\○○○○\△\□□\20230404大阪_購入候補 在庫報告.txt
  :
  :
ディレクトリE:\○○○○\△\□□\に上記のようなファイルがあり、月に30づつ増え
ていきます。
月に一度これらのファイルの日付と時間を変更します。(ファイル名中の数字8桁か
ら年4桁/月2桁/日2桁をつくり、時分は00:00:00固定)変更するのはファイルの日時
のみで、ファイル名は変わりません。

作業としては
(1)ディレクトリからファイルのフルパス一覧をコピー
(2)秀丸新規ページに張り付ける
(3)マクロで順次TOUCHコマンドを実行し、日時を書き換える
(4)2の新規ページを削除する
としています。

正常に動作するのですが、クリップボードの内容を配列に取り込めば、上記作業の
(2)と(4)が省けると思い、マクロの練習のため作ってみましたがうまくいきません。
エラーなどなく、なぜか最終行のみ実行されます。
コードに変なところがありましたら是非、ご教示ください。

//-------------------------------------------------------------
//クリップボードの内容を$a[0〜]の配列に取り込み順次処理する
loaddll "DengakuDLL.dll";//DengakuDLL.dllをHidemaruフォルダに入れて置くこと
if( !result ) {
message "DengakuDLL.dllのロードに失敗しました。";
endmacro;
}

 beginclipboardread; //データの取り込みを宣言
 #i = 0 ;
 $a[#i] = getclipboard();

 while( $a[#i] != "" ) { //データがなくなるまで繰り返す
//message $a[#i] ;

#sr0 = strstr($a[#i],"\\2",0) + 1 ; //1行目行頭から"\2"までの文字数取得。
//message str(#sr0) ;

//disabledraw;//画面再描画を止める
//文字列の8桁の 20230312 から 2023/03/12 形式の日付けを作る
$sr0 = midstr($a[#i],#sr0,4) ; //年の4桁を変数$sr0に入れる
$sr1 = midstr($a[#i],#sr0 + 4,2) ; //月の2桁を変数$sr1に入れる
$sr2 = midstr($a[#i],#sr0 + 6,2) ; //日の2桁を変数$sr2に入れる
$sr3 = $sr0 + "/" + $sr1 + "/" + $sr2 ; //合成して 2023/03/12 になるようにする
//message $sr3 ; //日付け確認

$opt1 = "/t " + $sr3 + "#00:00:00 " + $a[#i]; //時分秒はゼロに
//message $opt1 ; //文字列確認
#n = dllfunc("TOUCH", $opt1) ; //TOUCH実行

#i = #i + 1 ;
$a[#i] = getclipboard() ; //次のファイルへ
}
endmacro;
//------------------------------------------------------------

[ ]
RE:10119 クリップボード内容を配列で使うNo.10120
こみやんま さん 23/04/08 11:40
 
JSが利用できるかなり新しめの秀丸のバージョンなら、
「更新日時」をファイル名に従って変更するなら、

---------------------------------------------------------

js {

    try {
        debuginfo(2);
        var targetDir = "C:\\cccc2"; // 捜査対象のディレクトリ

        var shell = createobject("Shell.Application");
        var objFolder = shell.Namespace(targetDir); // ディレクトリのオブジ
ェクト
        if (!objFolder) {
             throw "該当のフォルダは存在しない";
        }
        var colItems = objFolder.Items(); // 該当のディレクトリに存在する一覧

        var regexp = /^(20\d{2})(\d{2})(\d{2})[^\d]/;    // ファイル名の対象
は「20dddddd(d以外)」なファイル。dとは数字

        // フォルダの中身のファイル群
        for(var ix = 0; ix < colItems.Count; ix++) {
            var fileobj = colItems.Item(ix); // そのファイルのオブジェクト
            if (fileobj.isFolder) { continue; } // 対象がファイルではなくフ
ォルダならパス。はい次のひと。
            var filename = fileobj.Name;     // そのオブジェクトの名前(=フ
ァイル名)
            // ファイル名は上のregexp のパターンを満たすのか?
            var ma = filename.match(regexp);
            if (ma) {
                // ファイル名からyear/month/day の文字列を抽出する。
                var year  = ma[1];
                var month = ma[2];
                var day   = ma[3];
                // ファイルのタイムスタンプを構築する。日本語順番にしておけ
ばそのスタンプになるから...
                var timestamp = sprintf("%s/%s/%s 00:00:00", year, month, day);
                //
                fileobj.ModifyDate = timestamp;
                console.log(filename + "\r\n");
            }
        }

    } catch(e) {
        console.log(e);
    }
}


---------------------------------------------------------

みたいなのにするのが真っ当な考え方なような。

---------------------------------------------------------

「更新日時」だけではなく、「作成日時も」変更するのだ、
ということなら
JavaScript経由やcreateobjectやrunex経由では道具不足しており迂遠なので、

hmJSかhmV8を使い(https://xn--pckzexbx21r8q9b.net/?page=nobu_tool_hm_javascript)


---------------------------------------------------------

#JS = loaddll(hidemarudir + @"\hmJS.dll");

if (!#JS) { message("hmjs.dllがない"); endmacro; }

#_ = dllfuncw(#JS, "DoString", R"JS(

var targetDir = "C:\\cccc2";
var list = clr.System.IO.Directory.GetFiles(targetDir, "*");

var regexp = /^(20\d{2})(\d{2})(\d{2})[^\d]/;    // ファイル名の対象は「20dd
dddd(d以外)」なファイル。

for(var ix = 0; ix < list.Length; ix++) {

    var filefullpath = list[ix]; // ファイルのフルパス
    var filename = clr.System.IO.Path.GetFileName(filefullpath); // フルパス
からファイル名部分のみを取り出す

    // ファイル名は上のregexp のパターンを満たすのか?
    var ma = filename.match(regexp);
    if (ma) {
        // ファイル名からyear/month/day の文字列を抽出する。
        var year  = ma[1];
        var month = ma[2];
        var day   = ma[3];

        // 年/月/日の表記からtimestamp作成。
        // ファイルのタイムスタンプを構築する。日本語順番にしておけばそのス
タンプになるから...
        var timestamp = sprintf("%s/%s/%s 00:00:00", year, month, day);
        var dt = clr.System.DateTime.Parse(timestamp);

        // 作成日時と更新日時の両方を更新
        clr.System.IO.File.SetCreationTime(filefullpath, dt);
        clr.System.IO.File.SetLastWriteTime(filefullpath, dt);
        hm.OutputPane.Output(filename + "\r\n");
    }
}

)JS");

freedll(#JS);

---------------------------------------------------------

あたりが素直な考え方だと思いますけどねー

[ ]
RE:10119 クリップボード内容を配列で使うNo.10121
igus さん 23/04/08 13:05
 
クリップボードで取り込んだ配列の末尾の改行コードが邪魔してるようですね。
最後の要素だけは改行コードが無くてうまく動作するということじゃないでしょうか。
一度別のマクロに書き出して調べてみて気付きました。

//===touch.mac===
$s=@"loaddll ""DengakuDLL.dll"";
if( !result ) {
message ""DengakuDLL.dllのロードに失敗しました。"";
endmacro;}
";

openfile "";insert $s;saveas currentmacrodirectory+ "\\tmp.mac",sjis;

beginclipboardread; //データの取り込みを宣言
#i = 0 ;
$a[#i] = getclipboard();

while( $a[#i] != "" ) { //データがなくなるまで繰り返す
  #sr0 = strstr($a[#i],"\\2",0) + 1 ; //1行目行頭から"\2"までの文字数取得。
  $sr0 = midstr($a[#i],#sr0,4) ; //年の4桁を変数$sr0に入れる
  $sr1 = midstr($a[#i],#sr0 + 4,2) ; //月の2桁を変数$sr1に入れる
  $sr2 = midstr($a[#i],#sr0 + 6,2) ; //日の2桁を変数$sr2に入れる
  $sr3 = $sr0 + "/" + $sr1 + "/" + $sr2 ; //合成して 2023/03/12 になるように
する
  $opt1 = "/t " + $sr3 + "#00:00:00 " + $a[#i]; //時分秒はゼロに
  insert "#n = dllfunc(\"TOUCH\", @\""+ $opt1 +"\");\n";
  #i = #i + 1 ;
  $a[#i] = getclipboard() ; //次のファイルへ
}
replaceallfast "txt\n","txt";save;
//#hwnd=hidemaruhandle(0);setactivehidemaru 1;
//closehidemaruforced #hwnd;
execmacro currentmacrodirectory+ "\\tmp.mac";
endmacro;
//===touch.mac===

自分なら秀丸マクロより rubyでバッチファイルをつくりたいケースで、これだと処
理したいフォルダに放り込んでダブルクリックで処理できます。

===touch.bat===
rem touch
ruby -x "%~f0"
goto end

#!ruby
$stderr=open("tmp.txt","w")

require 'fileutils'
Dir.glob('*.txt'){|e|
  if /^(\d{4})(\d{2})(\d{2})/=~e
    y,m,d=$1.to_i,$2.to_i,$3.to_i
    FileUtils.touch(e, mtime: Time.local(y,m,d))
  end
}
__END__
:end
===touch.bat===


[ ]
RE:10121 クリップボード内容を配列で使うNo.10122
伯林 さん 23/04/09 00:25
 
こみやんま さん
igus さん
レスありがとうございます。

>JS
とか
>rubyでバッチファイル
などは私の理解のレベルを超えていますので、igus さんのtouch.macを使わせていた
だきます。

>クリップボードで取り込んだ配列の末尾の改行コードが邪魔してるようですね。
には目からウロコがぽろりでした。

早速検証して わぉ!ブラボー と叫びました。感謝感謝です。

ところでtmp.macにはDengakuDLL.dll呼び出しの部分も表記されますがこれはなぜで
しょう?
よろしければお教えください。


[ ]
RE:10122 クリップボード内容を配列で使うNo.10123
igus さん 23/04/09 01:35
 
> ところでtmp.macにはDengakuDLL.dll呼び出しの部分も表記されますがこれはなぜ
>でしょう?
> よろしければお教えください。

TOUCHを使うので最初に DengakuDLL.dll をロードしています。
田楽DLLはほとんど使ったことがないのでこの辺は
伯林さんのマクロをそのまま使わせてもらいました。

「@"」から始まる文字列については秀丸マクロのヘルプの
「式について」−「文字列」を見てもらうといいのですが
エスケープが面倒とか改行をそのまま書きたいとかいう
場合に使うと便利です。
https://help.maruo.co.jp/hidemac/html/040_Statement_String.html

文字変数 $s にDLL呼び出しのための文字列を設定しておいて
無題のファイルを開いてから $s をinsert してます。

[ ]
RE:10123 クリップボード内容を配列で使うNo.10124
伯林 さん 23/04/09 05:15
 
ありがとうございました。

「@"」「R"」表記勉強になりました。

>文字変数 $s にDLL呼び出しのための文字列を設定しておいて
>無題のファイルを開いてから $s をinsert してます。

初めて配列というものを使いましたが、改行コードには思い至りませんでした。それ
にしてもいろんなやり方があるんだと奥深さにちょいと感激です。
これからもよろしくお願いいたします。

[ ]
RE:10119 クリップボード内容を配列で使うNo.10125
でるもんたいいじま さん 23/04/09 14:13
 
でるもんた・いいじまです。
出遅れました。

> $sr0 = midstr($a[#i],#sr0,4) ; //年の4桁を変数$sr0に入れる
> $sr1 = midstr($a[#i],#sr0 + 4,2) ; //月の2桁を変数$sr1に入れる
> $sr2 = midstr($a[#i],#sr0 + 6,2) ; //日の2桁を変数$sr2に入れる
> $sr3 = $sr0 + "/" + $sr1 + "/" + $sr2 ; //合成して 2023/03/12 になるように
>する
> //message $sr3 ; //日付け確認
>
> $opt1 = "/t " + $sr3 + "#00:00:00 " + $a[#i]; //時分秒はゼロに
> //message $opt1 ; //文字列確認
> #n = dllfunc("TOUCH", $opt1) ; //TOUCH実行

ここでコメントアウトしてあるmessage文ですが、たとえばこんな感じにすれば、行
末に改行が入っていることを検出できたはずです。
message "$opt1 = [" + $opt1 + "]";

あるいは、秀丸マクロにはsprintfという便利な関数がありますから、こんな書き方
もできます。
message sprintf("$opt1 = [%s]", $opt1);
sprintf()は他にも色々と便利な使い方ができますので、もし余裕があれば調べてみ
てください。

あとは、田楽DLLを使わなくても、こみやんまさんのコードのように Shell.Applicat
ion を使えばマクロだけで変更できそうです。
ただ残念ながら、ファイル名だけから File オブジェクトに辿りつくまでの方法をち
ょっと調べてみても、なかなか見つかりませんでした。

ちなみに私自身は、UNIX由来の touch.exe というコマンドを別途用意して、それで
タイムスタンプの変更をしています。

[ ]