作りたいもの
ドキュメントフォルダに大量のファイルが溜まってしまったため、次の機能を持ったプログラムを作成することにしました。
- 仕分け元フォルダ内(サブフォルダを含む)のファイルを更新日時に基づいて、別フォルダに整理する。
- 仕分け先には更新年月ごとのサブフォルダを作成し、ファイルはその中にコピーする。
- 重複するファイル(MD5ハッシュ値が同一のもの)は、ファイル名を変更して保存する。
Pythonコード
1.モジュールのインポートとロケールの設定
import os
import shutil
from datetime import datetime
import locale
import hashlib
from tkinter import Tk, filedialog
# ロケールを設定
locale.setlocale(locale.LC_ALL, 'ja_JP.UTF-8')
- shutilは、タイムスタンプの情報も含めてファイルをコピーするために使用しています。
- localeは、処理全体のロケールを日本語に設定するために使用しています。
- hashlibは、MD5ハッシュ値でファイルを比較するために使用しています。
- tkinterは、仕分け元、仕分け先フォルダをGUIで指定するために使用しています。
デフォルトでは英語に従って日付や時刻がフォーマットされるため、処理全体のロケールを日本語にしています。この部分は省略可能です。
2.ルートウィンドウの作成と非表示
# ルートウィンドウを非表示にする
root = Tk()
root.withdraw()
Tkinterを使ってダイアログボックスを表示するために、ルートウィンドウを作成して非表示にしています。
3.ダイアログボックスで仕分け元・先のフォルダを選択
# 仕分け元のフォルダをダイアログで選択する
src_folder = filedialog.askdirectory(title="仕分け元のフォルダを選択してください")
# 仕分け先のフォルダをダイアログで選択する
dst_folder = filedialog.askdirectory(title="仕分け先のフォルダを選択してください")
コードを実行すると、ダイアログが2回表示されます。1回目では仕分け元フォルダを、2回目は仕分け先フォルダを選択してください。
4.日付フォーマットの設定
# 仕分ける際のファイルの更新日時のフォーマット
date_format = "%Y-%m-%d %H:%M:%S.%f"
ファイルの更新日時を処理するためのフォーマットを設定しています。
5.仕分け処理
# 仕分けるフォルダ内のファイルを取得
for root, dirs, files in os.walk(src_folder):
for file in files:
# ファイルのパス
file_path = os.path.join(root, file)
# MD5ハッシュ値を取得
with open(file_path, 'rb') as f:
file_data = f.read()
file_hash = hashlib.md5(file_data).hexdigest()
# 仕分け先のフォルダパスを生成
mtime = os.path.getmtime(file_path)
mtime_obj = datetime.fromtimestamp(mtime)
year_month_str = mtime_obj.strftime("%Y年%m月")
dst_path = os.path.join(dst_folder, year_month_str)
# 仕分け先のフォルダが存在しなければ作成
os.makedirs(dst_path, exist_ok=True)
# 仕分け先に同じハッシュ値のファイルが存在する場合は、ファイル名を変更してコピーする
dst_file_path = os.path.join(dst_path, file)
i = 2
while os.path.exists(dst_file_path):
new_file_name = os.path.splitext(file)[0] + f" _重複{i}" + os.path.splitext(file)[1]
dst_file_path = os.path.join(dst_path, new_file_name)
i += 1
# ファイルをコピーする
shutil.copy2(file_path, dst_file_path)
1.os.walk() を使って、仕分け元のフォルダ内のすべてのファイルを取得します。
2.各ファイルについて以下の処理を行います。
- ファイルのパスを取得します。
- ファイルのMD5ハッシュ値を計算します。これは、後で重複ファイルを確認する際に使用します。
- ファイルの更新日時(mtime)を取得し、年と月に基づいて仕分け先のフォルダパスを生成します。
- 仕分け先のフォルダが存在しない場合は、新しく作成します。
- 仕分け先に同じハッシュ値のファイルが存在する場合、ファイル名を変更してコピーします。これは、重複するファイルが上書きされないようにするためです。
- 最後にファイルをコピーします。
この処理で、仕分け元のフォルダ内のファイルが、仕分け先のフォルダに更新日時に基づいて整理されます。具体的には、ファイルが更新された年と月に基づいて、仕分け先のフォルダ内にサブフォルダを作成し、ファイルを移動します。例えば、2022年9月に更新されたファイルは、仕分け先フォルダ内の「2022年09月」という名前のサブフォルダに移動されます。
また、重複したファイルの上書きを防ぎ、すべてのファイルを保持するため、ファイル名を変更して保存しています(重複ファイルが見つかった場合、ファイル名に、"_重複" という接尾辞と連番を追加しています)。
6.メモ
- このコードを書く時間を使って手動でファイルを整理すれば良かった。
- そもそも少しずつファイルを整理しておけば良かった。
- Pythonのコードを書くのが1年以上ぶりとなったため、一部のモジュールを使った処理についてChatGPT-4に助けてもらいました。便利な世の中になったものです。
以下、連結したコードです。
import os
import shutil
from datetime import datetime
import locale
import hashlib
from tkinter import Tk, filedialog
# ロケールを設定
locale.setlocale(locale.LC_ALL, 'ja_JP.UTF-8')
# ルートウィンドウを非表示にする
root = Tk()
root.withdraw()
# 仕分け元のフォルダをダイアログで選択する
src_folder = filedialog.askdirectory(title="仕分け元のフォルダを選択してください")
# 仕分け先のフォルダをダイアログで選択する
dst_folder = filedialog.askdirectory(title="仕分け先のフォルダを選択してください")
# 仕分ける際のファイルの更新日時のフォーマット
date_format = "%Y-%m-%d %H:%M:%S.%f"
# 仕分けるフォルダ内のファイルを取得
for root, dirs, files in os.walk(src_folder):
for file in files:
# ファイルのパス
file_path = os.path.join(root, file)
# MD5ハッシュ値を取得
with open(file_path, 'rb') as f:
file_data = f.read()
file_hash = hashlib.md5(file_data).hexdigest()
# 仕分け先のフォルダパスを生成
mtime = os.path.getmtime(file_path)
mtime_obj = datetime.fromtimestamp(mtime)
year_month_str = mtime_obj.strftime("%Y年%m月")
dst_path = os.path.join(dst_folder, year_month_str)
# 仕分け先のフォルダが存在しなければ作成
os.makedirs(dst_path, exist_ok=True)
# 仕分け先に同じハッシュ値のファイルが存在する場合は、ファイル名を変更してコピーする
dst_file_path = os.path.join(dst_path, file)
i = 2
while os.path.exists(dst_file_path):
new_file_name = os.path.splitext(file)[0] + f" _重複{i}" + os.path.splitext(file)[1]
dst_file_path = os.path.join(dst_path, new_file_name)
i += 1
# ファイルをコピーする
shutil.copy2(file_path, dst_file_path)