Pythonでファイル名を一括変更する方法|前後に文字列・連番を付ける、上書き事故を防ぐ確認手順

Pythonでファイル名を一括変更するには、pathlibのPath.glob()で対象ファイルを集め、with_name()やwith_stem()で新しい名前を作ってrename()します。前に付けるならp.with_name("前_" + p.name)、拡張子の前に付けるならp.with_stem(p.stem + "_後")、連番はenumerate()とf"{i:03d}"で作れます。

ただし一括変更は失敗すると元に戻すのが大変です。この記事では、基本の書き方に加えて、連番の順番がずれる原因(img10がimg2より前に来る問題)、変更先の名前がすでにあると上書きされる問題、実行前に変更内容を確認する方法まで、実行結果つきで紹介します。コードはPython 3.12.15(macOS)で実行しています。

練習用のファイルを用意する

いきなり本物のフォルダで試すのは危険なので、練習用のフォルダを作ってから試しましょう。以下の例はすべて、このphotosフォルダを作り直してから実行した結果です。

# 練習用のフォルダとファイルを作る
from pathlib import Path

folder = Path("photos")
folder.mkdir(exist_ok=True)
for name in ["img1.jpg", "img2.jpg", "img10.jpg", "memo.txt"]:
    (folder / name).touch()
print(sorted(p.name for p in folder.iterdir()))
['img1.jpg', 'img10.jpg', 'img2.jpg', 'memo.txt']

ファイル名の前に文字列を付ける

with_name()は、パスのファイル名部分だけを入れ替えた新しいパスを返します。フォルダの部分はそのまま残るので、rename()に渡しても別のフォルダへ移動してしまう心配がありません。

from pathlib import Path

folder = Path("photos")
for p in sorted(folder.glob("*.jpg")):
    new = p.with_name("2026_" + p.name)        # 前に文字列を付ける
    print(p.name, "->", new.name)
    p.rename(new)
img1.jpg -> 2026_img1.jpg
img10.jpg -> 2026_img10.jpg
img2.jpg -> 2026_img2.jpg

glob("*.jpg")で対象を拡張子で絞っているので、memo.txtは変わりません。

ファイル名の後ろ(拡張子の前)に文字列を付ける

stemは拡張子を除いた名前、suffixは拡張子です。with_stem()(Python 3.9以降)を使うと、拡張子を残したまま名前の部分だけを変えられます。

from pathlib import Path

folder = Path("photos")
for p in sorted(folder.glob("*.jpg")):
    new = p.with_stem(p.stem + "_edited")      # 拡張子の前に付ける
    print(p.name, "->", new.name)
    p.rename(new)
img1.jpg -> img1_edited.jpg
img10.jpg -> img10_edited.jpg
img2.jpg -> img2_edited.jpg

name・stem・suffixの違いはPython pathlibでファイル名・拡張子・親ディレクトリを取得する方法で詳しく解説しています。

名前の一部を置き換える

文字列のreplace()で新しい名前を作れば、名前の一部を一括で置き換えられます。

from pathlib import Path

folder = Path("photos")
for p in sorted(folder.glob("img*.jpg")):
    new = p.with_name(p.name.replace("img", "photo_"))   # 名前の一部を置き換える
    print(p.name, "->", new.name)
    p.rename(new)
img1.jpg -> photo_1.jpg
img10.jpg -> photo_10.jpg
img2.jpg -> photo_2.jpg

連番を付ける

そのまま並べると順番がずれる

glob()が返す順番は決まっていないので、連番を付けるときは必ず並べ替えます。ただしsorted()は名前を文字列として比べるため、「img10」が「img2」より前に来てしまいます。

from pathlib import Path

folder = Path("photos")
files = sorted(folder.glob("*.jpg"))
print([p.name for p in files])   # 文字列として並べると img10 が img2 より前に来る
['img1.jpg', 'img10.jpg', 'img2.jpg']

数字を数として並べてから連番を付ける

名前を数字とそれ以外に分け、数字の部分を整数として比べる関数をkeyに渡すと、人が見たとおりの順番(img1、img2、img10)になります。f"{i:03d}"は「3桁で、足りない分は0で埋める」という書式です。

import re
from pathlib import Path

def natural_key(p):
    # 数字の部分を数として比べる(img2 < img10)
    return [int(t) if t.isdigit() else t for t in re.split(r"(\d+)", p.name)]

folder = Path("photos")
files = sorted(folder.glob("*.jpg"), key=natural_key)
for i, p in enumerate(files, start=1):
    new = p.with_name(f"{i:03d}_{p.name}")     # 001_ のような連番を前に付ける
    print(p.name, "->", new.name)
    p.rename(new)
img1.jpg -> 001_img1.jpg
img2.jpg -> 002_img2.jpg
img10.jpg -> 003_img10.jpg

連番だけの名前にする

元の名前を捨てて「travel_01.jpg」のような連番の名前にそろえる場合は、p.suffixで拡張子だけを引き継ぎます。

import re
from pathlib import Path

def natural_key(p):
    return [int(t) if t.isdigit() else t for t in re.split(r"(\d+)", p.name)]

folder = Path("photos")
files = sorted(folder.glob("*.jpg"), key=natural_key)
for i, p in enumerate(files, start=1):
    new = p.with_name(f"travel_{i:02d}{p.suffix}")   # 連番だけの名前にする
    print(p.name, "->", new.name)
    p.rename(new)
img1.jpg -> travel_01.jpg
img2.jpg -> travel_02.jpg
img10.jpg -> travel_03.jpg

注意:変更先の名前がすでにあると上書きされる

Python公式ドキュメントによると、Path.rename()は、変更先がすでに存在するとき、Unix(macOS・Linux)では権限があれば黙って置き換え、WindowsではFileExistsErrorになります。macOSで試すと、確かに警告なしで上書きされました。

from pathlib import Path

folder = Path("photos")
a, b = folder / "img1.jpg", folder / "img2.jpg"
a.write_text("1枚目")
b.write_text("2枚目")

a.rename(b)    # 変更先がすでにある
print(sorted(p.name for p in folder.iterdir()))
print(b.read_text())
['img10.jpg', 'img2.jpg', 'memo.txt']
1枚目

img1.jpgの内容(1枚目)がimg2.jpgを上書きし、元のimg2.jpgの内容は失われています。連番を付け直すときや、名前を入れ替えるときに起きやすい事故です。

安全に一括変更する:先に変更内容を確認する

実務では「変更前と変更後の一覧を作る → 重複や既存ファイルとの衝突を調べる → 目で確認してから実行する」の順にすると安全です。次の例ではDRY_RUN = Trueの間は一覧を表示するだけで、ファイル名は変わりません。

import re
from pathlib import Path

def natural_key(p):
    return [int(t) if t.isdigit() else t for t in re.split(r"(\d+)", p.name)]

def plan_rename(folder, pattern, make_name):
    """変更前と変更後の組を作るだけで、まだ名前は変えない"""
    files = sorted(Path(folder).glob(pattern), key=natural_key)
    plan = [(p, p.with_name(make_name(i, p))) for i, p in enumerate(files, start=1)]
    olds = {p for p, _ in plan}
    news = [n for _, n in plan]
    if len(set(news)) != len(news):
        raise ValueError("変更後の名前が重複しています")
    for _, n in plan:
        if n.exists() and n not in olds:
            raise FileExistsError(f"すでに存在します: {n.name}")
    return plan

plan = plan_rename("photos", "*.jpg", lambda i, p: f"{i:03d}_{p.name}")
for old, new in plan:
    print(f"{old.name} -> {new.name}")

DRY_RUN = True          # 確認できたら False にして実行する
if not DRY_RUN:
    for old, new in plan:
        old.rename(new)
print(sorted(p.name for p in Path("photos").iterdir()))
img1.jpg -> 001_img1.jpg
img2.jpg -> 002_img2.jpg
img10.jpg -> 003_img10.jpg
['img1.jpg', 'img10.jpg', 'img2.jpg', 'memo.txt']

最後の行で、まだ元の名前のままであることがわかります。内容に問題がなければDRY_RUN = Falseにして実行します。あわせて、次の点にも気をつけましょう。

  • 大事なファイルは、先にフォルダごとコピーしてバックアップを取る
  • 変更後の名前がglob()の条件にも当てはまる場合は、先にリスト(sorted()の結果)にしてから回す。ここでの例はすべてsorted()でリストにしてから変更しています
  • Windowsでは変更先が存在するとエラーで止まるため、途中までしか変わっていない状態になりうる。上の例のように実行前に衝突を調べておく

os.rename(旧, 新)でも同じことができ、公式ドキュメントでもPath.rename()はos.rename()で実装されていると説明されています。ファイルを別のフォルダへ移したい場合はshutil.move()の使い方、ファイル操作全般はPythonのファイル操作まとめを参考にしてください。

よくある質問

Q. Pythonでファイル名を変更するには?

pathlibのPath.rename()を使います。Path(“old.txt”).rename(“new.txt”) のように書くと名前が変わり、新しいパスが返ります。os.rename(“old.txt”, “new.txt”) でも同じことができます。

Q. 拡張子を変えずに名前の後ろに文字を付けるには?

p.with_stem(p.stem + “_edited”) を使います。stemは拡張子を除いた名前なので、拡張子はそのまま残ります。with_stem()はPython 3.9以降で使えます。

Q. 連番を付けると順番がおかしくなるのはなぜですか?

sorted()がファイル名を文字列として比べるため、img10がimg2より前に来るからです。数字の部分を整数として比べるkey関数を渡して並べ替えてから、enumerate()で連番を付けます。

Q. rename()で同じ名前のファイルがあるとどうなりますか?

macOSやLinuxでは警告なしで上書きされ、元のファイルの内容は失われます。WindowsではFileExistsErrorになります。一括変更の前に、変更後の名前が既存のファイルと重ならないかを確認してください。

まとめ

ファイル名の一括変更は、glob()で集めてsorted()で並べ、with_name()やwith_stem()で新しい名前を作ってrename()する流れが基本です。連番は数字を数として並べてから付け、変更先がすでにあると上書きされる点に注意しましょう。いきなり実行せず、変更前と変更後の一覧を表示して確認してから実行するのが、失敗しないいちばんの近道です。

あわせて読みたい

学習の順序はPython学習のロードマップ、標準で使える機能の一覧はPython標準ライブラリのモジュール一覧にまとめています。

Pythonの関数・メソッドの全体像はPython基本関数・メソッド完全一覧にまとめています。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら