フォルダー内のファイル一覧をCSVで作成する
サブフォルダーを走査し、ファイルパス、拡張子、サイズ、更新日時を表として記録します。元のファイルと既存の結果をそのまま保持したまま、まず4つの小さなサンプルファイルで確認します。
新旧のファイル名を表で比較し、競合を確認します。明示的に --apply を指定した場合だけ新しいフォルダにコピーを作成し、元ファイルは保持します。
この翻訳はAIで作成しました。コード、単位、数値は原文と併せて確認してください。各言語のネイティブ話者による校閲は、まだ完了していません。 English
対象読者複数のファイル名を変更したいものの、誤った規則の適用やファイルの上書きを心配している初学者向けです。
batch operation では、1つの file の typo を見つけることよりも、誤った rule がすべての files に適用されるのを防ぐことが重要です。この例では plan を表示する前に name list 全体を検証します。default command は files を copy したり folders を作成したりしません。plan を確認した後、--apply を付けて operation を別途実行します。
python example.py| old_name | new_name |
|---|---|
| 메모 초안.txt | note-draft.txt |
| 견적 1.txt | quote-001.txt |
| 사진 설명.txt | photo-notes.txt |
各 mapping row は1つの original と1つの result を対応付けます。old_name は実際の filename と同じ表記で、letter case も含めて正確に入力してください。original を1つでも省略したり、存在しない name を追加したりすると operation 全体が停止します。この rule により、folder に新しい file を追加した後も古い mapping を使い続けるような mistake を検出できます。
new_name の values は、letter case だけが異なる場合でも conflict として扱います。これにより report.txt と REPORT.txt が別々の results になることを防ぎます。leading or trailing spaces、trailing dots、path separators、Windows reserved names も拒否されます。preview では、naming rules が task に合っているかを最後に人が確認する必要があります。
make_plan はすべての inputs と conflicts を確認します。main は validated plan を表示した後、--apply が指定されたかを確認します。plan を適用する場合でも originals に rename を使わず、新しい names で file contents を copy するため、開始時の状態を引き続き確認できます。
"""기본은 미리보기입니다. --apply일 때만 새 이름의 복사본을 만듭니다."""
import argparse
import csv
import re
import shutil
import sys
from pathlib import Path
BASE = Path(__file__).resolve().parent
INPUT = BASE / "source_files"
MAPPING = BASE / "mapping.csv"
OUTPUT = BASE / "outputs"
RESERVED = {"CON", "PRN", "AUX", "NUL"} | {
f"{prefix}{number}" for prefix in ("COM", "LPT") for number in range(1, 10)
}
def validate_name(name):
# 폴더 경로, Windows 예약 이름, 제어 문자 등을 이름으로 받지 않습니다.
if (not name or name in {".", ".."} or name != name.strip()
or name.endswith(".") or re.search(r'[<>:"/\\|?*\x00-\x1f]', name)
or name.split(".")[0].upper() in RESERVED):
raise ValueError(f"사용할 수 없는 파일명: {name!r}")
def make_plan():
if (INPUT.is_symlink() or not INPUT.is_dir()
or getattr(INPUT, "is_junction", lambda: False)()):
raise ValueError("source_files는 실제 폴더여야 합니다.")
if MAPPING.is_symlink():
raise ValueError("mapping.csv 링크는 처리하지 않습니다.")
sources = list(INPUT.iterdir())
if any(path.is_symlink() or not path.is_file() for path in sources):
raise ValueError("source_files에는 일반 파일만 넣으세요.")
if not sources:
raise ValueError("source_files에 파일이 없습니다.")
plan, old_keys, new_keys = [], set(), set()
with MAPPING.open("r", encoding="utf-8-sig", newline="") as stream:
reader = csv.DictReader(stream, strict=True)
if reader.fieldnames != ["old_name", "new_name"]:
raise ValueError("mapping.csv의 열은 old_name,new_name이어야 합니다.")
for row in reader:
if None in row or any(value is None for value in row.values()):
raise ValueError(f"mapping.csv {reader.line_num}행의 열 수를 확인하세요.")
old, new = row["old_name"], row["new_name"]
validate_name(old)
validate_name(new)
if old.casefold() in old_keys:
raise ValueError(f"원본이 중복 지정되었습니다: {old}")
if new.casefold() in new_keys:
raise ValueError(f"새 이름 충돌: {new}")
old_keys.add(old.casefold())
new_keys.add(new.casefold())
plan.append((old, new))
# 한 파일이라도 누락되거나 없는 파일을 지정하면 아무것도 복사하지 않습니다.
if {old for old, _ in plan} != {path.name for path in sources}:
raise ValueError("mapping.csv의 old_name은 원본 파일 전체와 정확히 일치해야 합니다.")
if OUTPUT.exists() or OUTPUT.is_symlink():
raise FileExistsError("outputs가 이미 있습니다. 기존 결과를 옮긴 뒤 실행하세요.")
return plan
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--apply", action="store_true", help="새 이름의 복사본을 outputs/renamed에 만듭니다.")
args = parser.parse_args()
plan = make_plan() # 충돌 검사는 출력 폴더를 만들기 전에 전체에 대해 끝냅니다.
for old, new in plan:
print(f"{old} → {new}")
if not args.apply:
print(f"미리보기: {len(plan)}개. 파일은 변경되지 않았습니다.")
print("이름을 확인한 뒤 python example.py --apply 를 실행하세요.")
return 0
OUTPUT.mkdir()
renamed = OUTPUT / "renamed"
renamed.mkdir()
for old, new in plan:
# 이름 변경 대신 복사합니다. xb는 대상이 생겼으면 덮어쓰지 않고 실패합니다.
with (INPUT / old).open("rb") as source, (renamed / new).open("xb") as target:
shutil.copyfileobj(source, target)
with (OUTPUT / "manifest.csv").open("x", encoding="utf-8-sig", newline="") as stream:
writer = csv.writer(stream)
writer.writerow(["old_name", "new_name"])
writer.writerows(plan)
print(f"완료: {len(plan)}개 복사본 → {renamed}")
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except (OSError, ValueError, csv.Error) as error:
print(f"중지: {error}", file=sys.stderr)
raise SystemExit(2)
argparse の store_true option により、--apply を省略した場合は preview が default になります。copy destinations は xb mode で開かれるため、すでに存在する file を上書きしません。完了した mapping は manifest.csv にも記録されるので、新しい name から original name を追跡できます。
3つの preview lines を確認したら、同じ folder で以下の command を実行します。preview 後に mapping.csv を編集した場合は、default command をもう一度実行して新しい plan を確認してください。apply command は前回の preview を記憶していません。実行時に mapping を再読み込みして検証します。
python example.py --apply| Location | 想定結果 |
|---|---|
| source_files/ | 既存 names のまま3つの original files を保持 |
| outputs/renamed/ | note-draft.txt, quote-001.txt, photo-notes.txt |
| outputs/manifest.csv | header の後に original → new name mapping rows が3行 |
terminal には “완료: 3개 Copy본” (complete: three copies) と output folder path が表示されます。各 new file を開き、name だけでなく contents も確認してください。たとえば note-draft.txt 内の practice document number は 메모 초안.txt の number と一致するはずです。
validation を copy loop 内で1 file ずつ行うと、2つ目で failure が発生する前に1つ目が処理される可能性があります。この例では最初にすべての names を確認するため、このような pre-check errors で partial application が発生しません。また、outputs がすでに存在する場合も開始前に停止します。
実務に適用するときは、まず1つの folder の小さな copy から始めてください。originals の copies を source_files に入れ、mapping.csv に file ごとに1 row を書きます。extension を含む full name を使用してください。extension を変えても file format は変換されないため、text file を .pdf で終わる name に mapping しないでください。
numbers を追加する場合は 001, 002 のように固定桁数を使い、dates は一貫した順序にすると results を見つけやすくなります。まず name に含める fields を決め、その後 preview で順序と omissions を確認します。この例では document contents から titles を抽出したり、intent を推測して names を自動生成したりはしません。
| Message | 確認すること | 解決方法 |
|---|---|---|
| New-name collision (새 이름 충돌) | 同一の new_name values、または letter case だけが異なる values | 重複しない final names を選びます。 |
| Original specified more than once | old_name が2 rows に重複 | original ごとに1 row だけ残します。 |
| Must match the complete set of original files | missing file または old_name の typo | folder contents と mapping を再度比較します。 |
| Invalid filename | Paths, reserved names, forbidden characters, trailing dots | filename だけを入力し、restricted characters を取り除きます。 |
| outputs already exists | 以前の copy operation の results | result folder を rename して保持してから、再度開始します。 |
plan を適用した後に同じ command を繰り返しても、既存 results は update されません。preview であっても existing outputs folder が見つかれば停止します。この rule により、別々の operations の results が混ざるのを防ぎます。overwrite option はありません。
この例では original files を読み取り、その copies に new names を付けるだけです。originals と copies の両方を保持するため、追加 storage が必要です。document contents は保持しますが、creation and modification times, access permissions, その他すべての operating-system metadata を複製する tool ではありません。
name conflicts など copy 前に見つかる errors は operation 全体を停止します。一度 copying が始まった後の power failure や insufficient disk space は、single transaction として rollback されません。一部の files が outputs に残る場合があるため、completion message と3つすべての files を確認してください。別 program による originals や mapping の concurrent changes も別の design が必要です。
2026-09-19 · Windows 11 · CPython 3.12.14 · 追加 packages なし · distribution の temporary copy で実行
コード、入力データ、実行手順が含まれています。展開して、まずREADME.txtを読んでください。
サンプルZIPをダウンロードサンプルコード、ファイル名、入力キーは原文のままです。翻訳本文のコマンドと確認手順も併せて参照してください。
独自に作成した練習用資料 · 元のファイルを別に保管してから実行してください。
説明と例は独自に作成しました。関連する動作や概念は、以下の公式資料で確認できます。