続・Pythonでファイル名の一括変換スクリプトを書いてみる


はじめに

前の記事でファイル名の一括変換スクリプトを作成しました。

が、結果として下記のような問題が起こっていました。

無事、一括変更することができましたが、
名前の対応が崩れてしまったようです。。。
※Pythonで変更前のファイル名を名前順でソートすると「コマンドプロンプト」の画像のような順番になるようですね。。。

今回はこの問題を解決することが目的となります。

何が問題だったのか

Pythonのsortに関して、いろいろ調べてみた結果、
「文字列」として数値を比較していることが原因のようです。
※文字列として比較する場合は"2"という文字列は"10"という文字列より大きい扱いとなっている

解決策

原因が分かったので、以下のような解決策を取ろうと思います。
①ファイル名を取得する(ファイル名中には数値が含まれるとする)
②ファイル名中の数値を取得し、文字列型ではなく数値型とする
③数値同士を比較して昇順で並び替え
④③を実施するときに元のファイル名も併せて昇順並び替え

さっそく書いてみる

①は前回と同様、glob関数を使用します。
②については追加で調査した結果、reモジュールのsub関数を使用すればよさそうです。
③は要は自力でリストを並び替えるだけですね

rename_r2.py
#モジュールのインポート
import os, glob, re

#ファイル名を取得したいファイルの拡張子を設定
#※ここでは「.jpg」
ext = ".jpg"

#ファイル名の一覧取得
#ここではrename.pyと同フォルダ内にある.jpgファイルを取得しています。
files = glob.glob("*"+ext)

#ファイル一覧格納配列の要素数を取得
files_size = len(files) 

#ファイル名中の数値を格納する配列を定義
num_list = list(range(files_size))

#数値格納配列用の要素カウンタを初期化
i = 0

#名前に使用する数値のカウンタを初期化
l = 0

#ファイル名の一覧に対して以下を繰り返し実施
#ファイル名一覧から1つを取り出して、変数「file_name」に格納する
for file_name in files:

    #file_name中の数字の個所を取得
    num = re.sub("\\D", "", file_name)

    #numは現在文字列型なので、数値型に変換して配列に格納する
    num_list[i] = int(num)

    #要素数カウンタをインクリメントする
    i += 1

#前述のfor文中で取得した数値を昇順で並び替える(バブルソート)
for j in range(files_size):
    for k in reversed(range(j+1,files_size)):

        #k番目とk-1番目を比較してk番目のほうが小さい場合は数値配列の
        #k番目とk-1番目を入れ替える ファイル名一覧配列についても同様
        if num_list[k-1] > num_list[k]:
            num_list[k-1],num_list[k] = num_list[k],num_list[k-1]
            files[k-1],files[k] = files[k],files[k-1]

#並び替えが完了したファイル名一覧に対して以下を繰り返し実施
#ファイル名一覧から1つを取り出して、変数「old_name」に格納する
for old_name in files:

    #変更後の名前のベースを設定
    #※ここでは「テスト_001」から数値が+1されていくように設定しています
    change_name = "テスト_"+"{0:03d}".format(l)

    #変更後の名称を変数「new_name」に格納する
    new_name = change_name + ext

    #ファイル名の変更
    os.rename(old_name, new_name)

    #新旧ファイル名の対応を表示
    print(old_name + "->" + new_name)

    #名前に使用する数値カウンタをインクリメント
    l += 1

テストしてみる

早速実行してみます。
変更前(1).jpg~変更前(18)がテスト_000.jpg~テスト_017.jpgとなれば成功です。

実行前のスクリーンショット

コマンドプロンプト

実行後のスクリーンショット

無事、一括変換することができました。
前回の問題点も解決され、想定通りに実行できています。

終わりに

名前の一括変更をPythonで実施してみました。
ファイル名にくっついている数値を参照して昇順で並び替えています。

今回の場合はファイル名中には数値が1箇所しかないので、この書き方で何とかなっていますが、
ファイル名中に数字箇所が複数出てくる場合(例:2021桜写真01.jpg)はどの箇所の数字を並び替えに使用するのかを決めて、
その部分の数字だけを格納し、並び替えに使う必要があります。
※この辺はre.sub()での取り方で何とかなりそうな気がするので、コードとしては書きません。

今後も何か思いついたらPythonで作ってみようと思います。