「20150328_203141_3600_004」(年月日_時分秒_区間秒_区間番号)
を「区間開始日時-区間終了日時」へ変換するJScript

ラベル:

前の関連記事:JScriptのWSH部分のインテリセンスを有効にする


予め決められた時間間隔で連番をつけて自動保存されるファイル名をその区間の開始時と終了時がわかるようなファイル名に変換したいと思います。変換元になるファイル名は1番目のファイルの開始日時、時間間隔、連番、で構成されています。

「年月日_時分秒_間隔_番号」から「開始年月日_時分秒-終了年月日_時分秒」を得る


jsファイルと同じディレクトリにある「20150328_203141_3600_004.wav」というファイル名を「20150328_233141-20150329_003141.wav」というファイル名に変換します。

変換元ファイル名「20150328_203141_3600_004.wav」は3600秒(=1時間)間隔で2015年3月28日20時31分41秒に1番目のファイルの自動保存が開始された4番目のファイルという意味です。

この情報を元にこの4番目のファイルの開始時と終了時を算出してそれをファイル名にします。

この例の場合は2015年3月28日20時31分41秒から3600秒x3経過した時点が開始時になり、終了時は開始時の3600秒後になります。

計算すると2015年3月28日23時31分41秒が開始時となり、終了時はその1時間後の2015年3月29日0時31分41秒になり、変更後のファイル名は「20150328_233141-20150329_003141.wav」になります。

参考資料を収集する


まずはカレントディレクトリからファイルコレクションの取得方法

指定階層の全ファイルを取得

カレントディレクトリの取得方法

カレントディレクトリを設定&取得

ファイルコレクションの使い方関連

Files コレクション
コレクションの要素は配列と違って要素番号ではとりだせません。

Enumerator オブジェクト
コレクションをEnumeratorオブジェクトにすることによって順番に要素を取り出します。

ここに書いてある2つの例はそのままでは動きませんでした。

例1は変数定義のところにあるコロンがエラーになります。TypeScriptと間違えている?

例2のfor (x in ファイルコレクション)でファイルコレクションの要素を取り出すことはできませんでした。(コレクションに対してfor...inが使えるのはJScript .NETのみです。 for...in ステートメント)

Files プロパティ

Windows管理者のためのWindows Script Host入門:第11回(最終回) WSHスクリプトからのファイル操作(2) (1/2) - @IT

ファイル名関連

ファイル名から拡張子を取得する - GetExtensionName [Scripting.FileSystemObject] - VBScript Tips [VBA/Access/Excel対応]

ファイル名からベース名を取得する - GetBaseName [Scripting.FileSystemObject] - VBScript Tips [VBA/Access/Excel対応]

対象ファイルの選別方法

正規表現(RegExp)

日付時刻の計算方法

日付と時刻の計算

Date()オブジェクトで日付設定するときに1月が0から始まり12月が11で終わることに注意が必要です。

getMonth()メソッドで月を取り出すときも1月が0、12月が11です。

getDay()メソッドは曜日番号を取り出すメソッドであって、日を取り出すメソッドはgetDate()です。

経過時間の計算は一旦ゼロ日時 (1970 年 1 月 1 日午前 00:00:00) から経過したミリ秒数に変換してから差をとります。

「日単位の経過時間の計算」の例はえらく面倒くさいことをしていますがそうしないといけないの?


日付形式を数字羅列に変換する方法

JavaScriptで日付や時間の0詰めを実装する - tagamidaiki.com

文字「0」を左から追加して右からの2文字を切り出しています。

ファイル名の変更方法

ファイル名を設定&取得

とりあえずWSH JScriptで作ってみた

var sh = new ActiveXObject("WScript.Shell");
var fso = new ActiveXObject("Scripting.FileSystemObject");
var fldr = fso.GetFolder(sh.CurrentDirectory); //カレントディレクトリオブジェクトの取得。
var fc = new Enumerator(fldr.Files); //ファイルコレクションをEnumeratorオブジェクトにして取得。
var f, n, fn, start_dt, int_sec, int_cnt, start_ms, start_n, end_n, new_name;
var end_dt = new Date();
var ext = "wav"; //改名対象拡張子。
//ファイルコレクションの各ファイルに対して。
for (; !fc.atEnd() ; fc.moveNext()) { 
    f = fc.item(); //ファイルオブジェクトの取得。
    fn = f.Name; //ファイル名の取得。
    //拡張子の判別。
    if (fso.GetExtensionName(fn) == ext) {
        n = fso.GetBaseName(fn); //拡張子を除くファイル名を取得。
        //ファイル名の判別
        if (n.match(/^\d{8}_\d{6}_\d+_\d+$/)) {
            //ファイル名から記録開始日時のDateオブジェクトを得る。1月を0として開始。
            start_dt = new Date(n.slice(0, 4), n.slice(4, 6)-1, n.slice(6, 8), n.slice(9, 11), n.slice(11, 13), n.slice(13, 15));
            int_sec = n.slice(16, -4) * 1000; //記録区間長(ミリ秒)
            int_cnt = n.slice(-3); //記録区間番号
            start_ms = start_dt.getTime() + int_sec * (int_cnt - 1); //区間開始日時経過ミリ秒
            end_dt.setTime(start_ms + int_sec); //区間終了日時Dateオブジェクトを取得)
            start_dt.setTime(start_ms); //区間開始日時Dateオブジェクトを取得。
            //日付形式を数字羅列に変換
            start_n = start_dt.getFullYear() + ("0" + (start_dt.getMonth() + 1)).slice(-2) + ("0" + (start_dt.getDate())).slice(-2) + "_" + ("0" + (start_dt.getHours())).slice(-2) + ("0" + (start_dt.getMinutes())).slice(-2) + ("0" + (start_dt.getSeconds())).slice(-2);
            end_n = end_dt.getFullYear() + ("0" + (end_dt.getMonth() + 1)).slice(-2) + ("0" + (end_dt.getDate())).slice(-2) + "_" + ("0" + (end_dt.getHours())).slice(-2) + ("0" + (end_dt.getMinutes())).slice(-2) + ("0" + (end_dt.getSeconds())).slice(-2);
            new_name = start_n + "-" + end_n + "." + ext;//新しいファイル名の作成。
            //f.Name = new_name;//ファイル名を変更。
            WScript.Echo(fn + "を" + new_name + "に改名");
        }
    }
}
これを拡張子jsのファイルで保存します。auto_rec_rename.jsとしました。

Visual Studioで保存するときは、ファイル→保存オプションの詳細設定、でエンコードもUTF-8からシフトJISに変更しておく必要があります。

テスト用なので実際に改名している28行目はコメントアウトしています。

JScriptのWSH部分のインテリセンスを有効にするで作ったVisual Studio Community 2013の「JScript起動」ボタンを使うとコマンドウィンドウに以下のように表示されます。

20150328_203120_3600_001.wavを20150328_203120-20150328_213120.wavに改名
20150328_203141_3600_004.wavを20150328_233141-20150329_003141.wavに改名
続行するには何かキーを押してください . . .

同じフォルダに20150328_203120_3600_001.wavと20150328_203141_3600_004.wavがある場合です。

28行目のコメントアウトをはずすとこの通りにファイル名が書き換えられます。

「キャプチャする括弧」を使ってファイル名から日付を取り出す


正規表現で丸括弧「キャプチャする括弧」を使って日付書式を整形するでやった正規表現を使って18行目の長ったらしい部分をすっきりさせます。
var sh = new ActiveXObject("WScript.Shell");
var fso = new ActiveXObject("Scripting.FileSystemObject");
var fldr = fso.GetFolder(sh.CurrentDirectory); //カレントディレクトリオブジェクトの取得。
var fc = new Enumerator(fldr.Files); //ファイルコレクションをEnumeratorオブジェクトにして取得。
var f, n, fn, start_dt, int_sec, int_cnt, start_ms, start_n, end_n, new_name;
var end_dt = new Date();
var re = /^(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})_(\d+)_(\d+)$/;//ファイル名から情報を取り出す正規表現リテラル。
var ext = "wav"; //改名対象拡張子。
//ファイルコレクションの各ファイルに対して。
for (; !fc.atEnd() ; fc.moveNext()) {
    f = fc.item(); //ファイルオブジェクトの取得。
    fn = f.Name; //ファイル名の取得。
    //拡張子の判別。
    if (fso.GetExtensionName(fn) == ext) {
        n = fso.GetBaseName(fn); //拡張子を除くファイル名を取得。
        //ファイル名の判別
        if (re.test(n)) {
            r = re.exec(n);//記録区間長と記録区間番号を抽出する正規表現。
            //ファイル名から記録開始日時のDateオブジェクトを得る。1月を0として開始。
            start_dt = new Date(r[1], r[2] - 1, r[3], r[4], r[5], r[6]);
            int_sec = n.slice(16, -4) * 1000; //記録区間長(ミリ秒)
            int_cnt = n.slice(-3); //記録区間番号
            start_ms = start_dt.getTime() + int_sec * (int_cnt - 1); //区間開始日時経過ミリ秒
            end_dt.setTime(start_ms + int_sec); //区間終了日時Dateオブジェクトを取得)
            start_dt.setTime(start_ms); //区間開始日時Dateオブジェクトを取得。
            //日付形式を数字羅列に変換
            start_n = start_dt.getFullYear() + ("0" + (start_dt.getMonth() + 1)).slice(-2) + ("0" + (start_dt.getDate())).slice(-2) + "_" + ("0" + (start_dt.getHours())).slice(-2) + ("0" + (start_dt.getMinutes())).slice(-2) + ("0" + (start_dt.getSeconds())).slice(-2);
            end_n = end_dt.getFullYear() + ("0" + (end_dt.getMonth() + 1)).slice(-2) + ("0" + (end_dt.getDate())).slice(-2) + "_" + ("0" + (end_dt.getHours())).slice(-2) + ("0" + (end_dt.getMinutes())).slice(-2) + ("0" + (end_dt.getSeconds())).slice(-2);
            new_name = start_n + "-" + end_n + "." + ext;//新しいファイル名の作成。
            //f.Name = new_name;//ファイル名を変更。
            WScript.Echo(fn + "を" + new_name + "に改名");
        }
    }
}
7行目で正規表現リテラルで正規表現パターンを定義しています。

あとで取り出したい部分を丸括弧で囲っています。これがキャプチャする括弧です。

17行目はtestメソッドでnにreに一致するものがあるかどうかテストしています。(正規表現の使用

正規表現パターンに一致するファイル名であればtrueが返ります。

18行目でファイル名を正規表現パターンで検索し結果の配列をrに収納します。

20行目、21行目、22行目でその結果を配列から取り出しています。

これでだいぶすっきりしました。

ファイル名は拡張子を別にして正規表現を当てはめていますが、次に拡張子も含めて正規表現を当てはめてしまいます。

拡張子は8行目で変数extに入れています。

変数を使って正規表現パターンを定義したいときは正規表現リテラルではなくRegExpオブジェクトを使います。(正規表現の作成
var sh = new ActiveXObject("WScript.Shell");
var fso = new ActiveXObject("Scripting.FileSystemObject");
var fldr = fso.GetFolder(sh.CurrentDirectory); //カレントディレクトリオブジェクトの取得。
var fc = new Enumerator(fldr.Files); //ファイルコレクションをEnumeratorオブジェクトにして取得。
var f, n, start_dt, int_sec, int_cnt, start_ms, start_n, end_n, new_name;
var end_dt = new Date();
var ext = "wav"; //改名対象拡張子。
//RegExpオブジェクトを作成。
var re = new RegExp("^(\\d{4})(\\d{2})(\\d{2})_(\\d{2})(\\d{2})(\\d{2})_(\\d+)_(\\d+)\\." + ext +"$")
//ファイルコレクションの各ファイルに対して。
for (; !fc.atEnd() ; fc.moveNext()) {
    f = fc.item(); //ファイルオブジェクトの取得。
    n = f.Name; //ファイル名の取得。
    //ファイル名の判別
    if (re.test(n)) {
        r = re.exec(n);//記録区間長と記録区間番号を抽出する正規表現。
        //ファイル名から記録開始日時のDateオブジェクトを得る。1月を0として開始。
        start_dt = new Date(r[1], r[2] - 1, r[3], r[4], r[5], r[6]);
        int_sec = r[7] * 1000; //記録区間長(ミリ秒)
        int_cnt = r[8]; //記録区間番号
        start_ms = start_dt.getTime() + int_sec * (int_cnt - 1); //区間開始日時経過ミリ秒
        end_dt.setTime(start_ms + int_sec); //区間終了日時Dateオブジェクトを取得)
        start_dt.setTime(start_ms); //区間開始日時Dateオブジェクトを取得。
        //日付形式を数字羅列に変換
        start_n = start_dt.getFullYear() + ("0" + (start_dt.getMonth() + 1)).slice(-2) + ("0" + (start_dt.getDate())).slice(-2) + "_" + ("0" + (start_dt.getHours())).slice(-2) + ("0" + (start_dt.getMinutes())).slice(-2) + ("0" + (start_dt.getSeconds())).slice(-2);
        end_n = end_dt.getFullYear() + ("0" + (end_dt.getMonth() + 1)).slice(-2) + ("0" + (end_dt.getDate())).slice(-2) + "_" + ("0" + (end_dt.getHours())).slice(-2) + ("0" + (end_dt.getMinutes())).slice(-2) + ("0" + (end_dt.getSeconds())).slice(-2);
        new_name = start_n + "-" + end_n + "." + ext;//新しいファイル名の作成。
        //f.Name = new_name;//ファイル名を変更。
        WScript.Echo(n + "を" + new_name + "に改名");
    }
}
9行目で正規表現パターンを引数としてRegExpオブジェクトのコンストラクタ関数をnew演算子で呼び出して新しいオブジェクトを生成しています。(クラスベースの言語だとこれをインスタンス化といます。)

RegExpコンストラクタ関数の引数は文字列になるので、正規表現パターンのエスケープ文字の\自体もエスケープしないといけません。

これでファイル名の判定が1回で済むようになりました。

参考にしたサイト


正規表現 - JavaScript | MDN
JScriptでも同じように使えます。

次の関連記事:WSH JScriptでJavaScriptの学習環境を整える

PR

0 件のコメント:

コメントを投稿