日常の業務やプライベートでのデータ整理において、大量のファイル名を一つずつ手作業で変更するのは非常に手間がかかる作業です。
特に数百から数千単位のファイルを扱う場合、手動での作業は時間の浪費であるだけでなく、ヒューマンエラーの原因にもなります。
こうした単純作業こそ、プログラミングによる自動化が最も真価を発揮する場面です。
Pythonにはファイル操作を直感的に、かつ安全に行うための標準ライブラリが充実しています。
かつてはosモジュールが主流でしたが、現在のモダンなPython開発においては、オブジェクト指向でパスを操作できる「pathlib」モジュールの使用が推奨されています。本記事では、このpathlibを活用して、特定の文字列の置換や連番の付与、拡張子の変更などを効率化する具体的な手法を詳しく解説していきます。
ファイル名の一括変更にPythonが最適な理由
ファイル名の一括変更プログラムを作成する際、Pythonが選ばれる理由は、その「読みやすさ」と「汎用性の高さ」にあります。
複雑な正規表現を用いたパターンマッチングから、連番のゼロ埋め(パディング)、日付情報の付与まで、標準ライブラリのみで完結できる点が大きなメリットです。
また、WindowsやmacOS、Linuxといった異なるOS間でも、パスの区切り文字の違いなどをライブラリが吸収してくれるため、一度作成したスクリプトを環境を選ばずに使い回すことが可能です。
これから紹介する手法をマスターすれば、手作業では数時間かかる処理も、実行ボタン一つで数秒のうちに完了させることができるようになります。
pathlibモジュールの基本概念とメリット
Python 3.4から導入されたpathlibは、ファイルシステム上のパスを単なる文字列としてではなく、一つの「オブジェクト」として扱うライブラリです。
osモジュールとの違い
以前から利用されているos.pathなどのモジュールでは、パスの結合や親ディレクトリの取得を行う際に、文字列演算や関数を何層にもネストさせる必要がありました。
一方、pathlibでは/(スラッシュ)演算子を使って直感的にパスを結合したり、プロパティを呼び出すだけでファイル名や拡張子を抽出したりできます。
| 機能 | osモジュール (旧) | pathlibモジュール (現行) |
|---|---|---|
| パスの結合 | os.path.join(a, b) | a / b |
| ファイル名の取得 | os.path.basename(p) | p.name |
| 拡張子なしのファイル名 | os.path.splitext(n)[0] | p.stem |
| 拡張子の取得 | os.path.splitext(n)[1] | p.suffix |
Pathオブジェクトの操作
pathlibを使用するには、まずPathクラスをインポートし、対象となるパスをインスタンス化します。
from pathlib import Path
# 現在のディレクトリを基準としたパスオブジェクトの作成
p = Path("data/sample.txt")
print(f"ファイル名: {p.name}")
print(f"ファイル名(拡張子なし): {p.stem}")
print(f"拡張子: {p.suffix}")
ファイル名: sample.txt
ファイル名(拡張子なし): sample
拡張子: .txt
このように、p.nameやp.stemといった直感的な属性アクセスにより、ファイル名の構成要素を簡単に取得できるのが最大の特徴です。
実践1:特定の文字列を置換して一括変更する
まずは、ファイル名に含まれる特定のキーワードを、別の文字列に一括置換する手法を見ていきましょう。
例えば、プロジェクト名が変更になった場合や、古い日付を一新したい場合などに非常に有効です。
以下のサンプルコードでは、カレントディレクトリ内にある「2025」という文字列を含むすべてのファイルを検索し、それを「2026」に書き換えます。
from pathlib import Path
def replace_filenames(target_dir, old_str, new_str):
# 対象ディレクトリのPathオブジェクトを作成
base_path = Path(target_dir)
# ディレクトリ内のすべてのファイルをループで処理
# iterdir() はディレクトリ内の内容を走査する
for file_path in base_path.iterdir():
# ファイルであり、かつ古い文字列が名前に含まれているかチェック
if file_path.is_file() and old_str in file_path.name:
# 新しいファイル名を作成(replaceメソッドを使用)
new_name = file_path.name.replace(old_str, new_str)
# with_nameメソッドで新しいパスオブジェクトを生成
new_file_path = file_path.with_name(new_name)
# renameメソッドで実際に名前を変更
file_path.rename(new_file_path)
print(f"変更完了: {file_path.name} -> {new_name}")
# カレントディレクトリの 'work_files' フォルダ内を処理する場合
# replace_filenames('work_files', '2025', '2026')
このコードのポイントは、with_nameメソッドを使用している点です。
これは、元のパスのディレクトリ部分は保持したまま、ファイル名の部分だけを新しいものに置き換えた新しいPathオブジェクトを返します。
これにより、ディレクトリ構造を崩さずにファイル名だけを安全に操作できます。
実践2:ファイル名に連番を付与する自動化
次に、デジカメの画像データやログファイルなど、バラバラな名称のファイルに対して、整理しやすいように「任意の名前 + 連番」という形式に一括変更する方法を解説します。
ここでは、ファイルの作成日時順に並べ替えた上で、「item_001.jpg」のようにゼロ埋めされた3桁の連番を付与するロジックを実装します。
from pathlib import Path
def add_serial_numbers(target_dir, base_name):
path = Path(target_dir)
# 拡張子が .jpg のファイルを取得し、作成日時順にソート
# glob() を使うと特定のパターンに一致するファイルを抽出可能
files = sorted(path.glob("*.jpg"), key=lambda f: f.stat().st_ctime)
for i, file_path in enumerate(files, start=1):
# 3桁のゼロ埋め連番を作成 (例: 001, 002...)
extension = file_path.suffix
new_name = f"{base_name}_{i:03d}{extension}"
# リネーム実行
new_path = file_path.with_name(new_name)
file_path.rename(new_path)
print(f"Renamed: {file_path.name} to {new_name}")
# 実行例: 'photos'フォルダ内のJPGファイルに 'trip' という名前で連番を振る
# add_serial_numbers('photos', 'trip')
Renamed: IMG_8823.jpg to trip_001.jpg
Renamed: DSC0012.jpg to trip_002.jpg
Renamed: 20260505.jpg to trip_003.jpg
enumerate(files, start=1) を使用することで、ループの中でインデックスを1から自動的にカウントアップしています。
また、f"{i:03d}" という書式指定子を用いることで、数値を簡単にゼロ埋め文字列に変換できるのがPythonの便利なところです。
実践3:拡張子を一括変換する手法
開発現場やデザインの現場では、「大量にある .txt ファイルをすべて .md (Markdown) に変更したい」「古い .jpeg を .jpg に統一したい」といったニーズが頻繁に発生します。
pathlibには拡張子を変更するための専用メソッド with_suffix が用意されており、これを使うと非常に簡潔に記述できます。
from pathlib import Path
def change_extensions(target_dir, old_ext, new_ext):
path = Path(target_dir)
# 拡張子の前にドットがない場合は付与する
old_ext = f".{old_ext}" if not old_ext.startswith(".") else old_ext
new_ext = f".{new_ext}" if not new_ext.startswith(".") else new_ext
# 指定した拡張子のファイルをリストアップ
for file_path in path.glob(f"*{old_ext}"):
# with_suffix は拡張子だけを差し替えたパスを返す
new_path = file_path.with_suffix(new_ext)
file_path.rename(new_path)
print(f"拡張子変更: {file_path.name} -> {new_path.name}")
# 実行例: .txt を .md に変更
# change_extensions('docs', 'txt', 'md')
with_suffix メソッドは、引数に新しい拡張子(ドットを含む)を渡すだけで、元のファイル名から拡張子部分だけを賢く置換してくれます。
万が一、元のファイル名に複数のドットが含まれていても、最後のドット以降を対象とするため、誤動作のリスクが低いのがメリットです。
安全なスクリプト運用のための注意点
ファイル名の一括変更は、一度実行してしまうと元に戻す(Undo)のが大変な作業です。
特にプログラムによる自動化では、一瞬で全てのファイルが書き換わってしまうため、実行前に慎重な確認が必要になります。
ファイルの重複チェック
リネーム後の名前が、既にそのディレクトリ内に存在するファイル名と重複してしまった場合、多くのOS環境では既存のファイルが上書きされて消えてしまう危険があります。
if new_path.exists():
print(f"警告: {new_path.name} は既に存在するためスキップします。")
continue
このように、renameメソッドを呼ぶ前に、exists()メソッドを用いて同名のファイルが存在しないか確認するロジックを組み込むことを強く推奨します。
実行前のシミュレーション(ドライラン)
プログラムをいきなり本番環境で動かすのではなく、まずは「どのファイルがどう変わるか」を表示するだけの「ドライラン(シミュレーション)」を行う機能を付けておくと安心です。
def rename_with_dry_run(file_path, new_path, dry_run=True):
if dry_run:
print(f"[DRY RUN] {file_path.name} -> {new_path.name}")
else:
file_path.rename(new_path)
print(f"[EXECUTED] {file_path.name} -> {new_path.name}")
このようにフラグ一つで実際の処理を切り替えられるように設計しておけば、まずはコンソール出力で結果を確認し、問題がないことを確信してから本番実行に移ることができます。
まとめ
本記事では、Pythonのpathlibモジュールを活用して、ファイル名を一括で変更するさまざまな手法を紹介しました。
文字列の置換、連番の付与、そして拡張子の変更といった処理は、いずれも業務効率化の第一歩として非常に効果的です。
osモジュールからpathlibへの移行により、コードはより読みやすく、保守性の高いものになりました。
ファイル名の一括操作スクリプトを一つ持っておくだけで、これまで費やしていた退屈な手作業の時間はなくなり、よりクリエイティブなタスクに集中できるようになります。
まずは簡単な置換処理から試してみて、Pythonによる自動化の便利さを実感してみてください。
最後に、自動化スクリプトを作成する際は、常にバックアップを取り、ドライランで動作を確認する習慣を忘れずに。
安全で確実な自動化が、エンジニアリングにおける最も重要なスキルのひとつです。
