ファイル名先頭についた3ケタ数字を2ずつ進める

2017年08月22日

@AJABONさん(http://ajabon.catfood.jp/)からのヘルプ

ぐぎゃー、ファイル名先頭についた3ケタ数字を2ずつ進めるやつください」

に回答したのでここにもメモしておきます。

var reg = /^[0-9]{3}/; //正規表現パターン。\dは処理系でマッチする範囲が違うので使いたくない
var rnlist = []; //変更したいファイル名のリスト
// [[ファイル名1変更前, ファイル名1変更後], [ファイル名2変更前, ファイル名2変更後], [ファイル名3変更前, ファイル名3変更後], ...]

tFolder = Folder.selectDialog("フォルダを選んでください"); //対象のフォルダを選択させる
var tFiles = tFolder.getFiles(); //フォルダ内のファイルリスト
for (i = 0; i < tFiles.length; i++) {
    //ファイル名が正規表現パターンに一致したら
    if (tFiles[i].name.match(reg)) {
     //変更したいファイル名のリストに値を追加
        rnlist.push([
            //ファイル名変更前
            tFiles[i].name, 
            //ファイル名変更後
            tFiles[i].name.replace(reg, function ($0) {
                var str = String(Number($0) + 2);
                while (str.length < 3) {str = "0" + str;}
                return str;
            })
        ]);
    }
}
rnlist.sort(); //配列をファイル名の昇順でソート
//配列の後ろから名前変更を実行
for (i = rnlist.length - 1; i > -1; i--) {
    File(tFolder.fsName + "/" + rnlist[i][0]).rename(rnlist[i][1]);
}

いくつか解説を。

tFiles[i].name.replace(reg, function ($0) {
    var str = String(Number($0) + 2);
    while (str.length < 3) {
        str = "0" + str;
    }
    return str;
}) 

変更後のファイル名を作成します。$0は正規表現パターンにマッチした文字列が入ります。それをいじくる訳ですが、ポイントは「3桁に満たない数値をどうやって3桁の文字列にするか」。自分のやり方は、まず素直に数値に変換して2を加えて文字列に戻します。それを3桁になるまで先頭に「0」文字を追加する方法です。これ意外と気づかない。

あと、replace関数の中で関数を記述するのはあるふぁ(仮)さん(http://sysys.blog.shinobi.jp/)に教えてもらいました。VBScriptの感覚でやってるとこれをなかなか理解できないので、これは自分の備忘録です。

rnlist.sort();

ファイルのリストを取得した時点で、ファイル名でソートされているようなんですが、確信がなかったので、ここで改めてファイル名順に並び替えています。なぜ並び替えるかというと次の項目で解説します。

for (i = rnlist.length - 1; i > -1; i--) {
    File(tFolder.fsName + "/" + rnlist[i][0]).rename(rnlist[i][1]);
}

ファイルリストの後ろから処理しています。なぜかというと、ファイル名の数値を増やすから。もし仮に「001.txt, 003.txt」だった場合、先頭から処理すると、変更後のファイル名が既に存在するファイルと同じになってエラーになってしまいます。これを後ろから処理するとエラーが発生しないということですね。逆に、ファイル名の数字を減らすということであれば先頭から処理します。

こういうことを自然にできるようになると、肌感覚でプログラムをしているという実感が持てて、一人悦に入っています。

追記

var str = String(Number($0) + 2);
while (str.length < 3) {
    str = "0" + str;
}
return str;

の部分を次のように書く方法もあります。

return String(Number($0) + 2 + 1000).slice(1);

こちらの方が文字数が少ないので、簡単そうに見えるかも。見た通り、1000を足してから文字列に変換、2文字目以降を取り出す手順です。

JavaScriptの場合はこちらの方がいいかも知れませんね。多分速いし。問題は、桁数に変更があった場合に修正し忘れる可能性が高い(1000を「10の3乗」と書けばいいんですけど)というのと、他の処理系IntegerLong Integer型があるような場合だと、使用できる桁数を気にしなきゃいけない(下手するとオーバーフローでエラーになる)ので、自分としてはやや使いにくい方法ではあります。

追記の追記

Twitterであるふぁ(仮)さんより

return String(Number($0) + 2 + 1000).slice(1);

の部分は

return ("000"+($0-0+2)).slice(-3);

と書いた方が短くなるよ、とご指摘をいただきました。自分でも試したところ、ExtendScriptでは

return ("000"+(+$0+2)).slice(-3);

でも行けましたので、短いのが好きな方はどうぞ。