CSVファイルを結合し、元のファイル名を保持する
同じ列構成のCSVファイルを順番に結合し、source_file列を追加します。小さなデータセットを使って、カンマを含む項目名、列不足、既存の出力フォルダを確認します。
サブフォルダーを走査し、ファイルパス、拡張子、サイズ、更新日時を表として記録します。元のファイルと既存の結果をそのまま保持したまま、まず4つの小さなサンプルファイルで確認します。
この翻訳はAIで作成しました。コード、単位、数値は原文と併せて確認してください。各言語のネイティブ話者による校閲は、まだ完了していません。 English
対象読者フォルダー内の文書数や保存場所を手作業で一覧化してきたPython初心者
出力フォルダーを作成しただけでは、この作業が完了したとはいえません。CSVには4つの元ファイルがそれぞれ正確に1回ずつ含まれ、一覧に記録されたサイズの合計が63バイトであり、元の内容が変更されていない必要があります。自分の作業フォルダーに進む前に、同梱データでこれらの条件を確認してください。
python example.pyコードは現在のターミナルフォルダーではなく、example.py の場所を基準に開始します。BASE 配下の sample_files だけを走査するため、内部の配置を変えなければサンプルフォルダー全体を移動できます。outputs は走査対象フォルダーの外にあるため、後で再実行しても結果CSVが入力一覧に含まれません。
| 列 | 意味 | 確認する点 |
|---|---|---|
| relative_path | sample_files からの相対パス | 同じ名前のファイルでも、別のフォルダーにあれば区別できます。 |
| extension | 最後の拡張子を小文字で表示 | 拡張子がない場合は (없음) と表示され、tar.gz は .gz と表示されます。 |
| size_bytes | 整数のバイト数で表したファイルサイズ | 0は欠損値ではなく、空のファイルを意味します。 |
| modified_utc | UTCでのファイル更新日時 | 末尾の +00:00 はUTCを意味し、韓国の現地時刻とは異なります。 |
relative_path は、OSをまたいで読みやすくするためにスラッシュを使用します。結果はこのパスで並べ替えられます。内部のファイル走査順序には依存しないため、同じ入力に対する行順を比較しやすくなります。
collect_files はファイル情報を収集し、main は新しいCSVを書き出します。入力の確認が完了するまで outputs は作成されません。読み取れないフォルダーやリンクに遭遇した場合は、不完全な一覧を正常な結果として保存せずに停止します。
"""sample_files를 읽어 outputs/file_list.csv에 목록을 기록합니다. 원본은 변경하지 않습니다."""
import csv
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
BASE = Path(__file__).resolve().parent
INPUT = BASE / "sample_files"
OUTPUT = BASE / "outputs"
FIELDS = ["relative_path", "extension", "size_bytes", "modified_utc"]
def is_link(path):
# Windows 연결 디렉터리(junction)도 Python 3.12 이상에서는 거부합니다.
return path.is_symlink() or getattr(path, "is_junction", lambda: False)()
def collect_files(folder):
if is_link(folder) or not folder.is_dir():
raise ValueError("sample_files는 실제 폴더여야 합니다.")
rows = []
def stop_on_error(error):
raise error
# 하위 폴더까지 읽되, 링크를 따라 다른 폴더로 나가지 않습니다.
for root, dirs, files in os.walk(folder, followlinks=False, onerror=stop_on_error):
root = Path(root)
for name in dirs + files:
if is_link(root / name):
raise ValueError(f"링크는 처리하지 않습니다: {root / name}")
for name in sorted(files):
path = root / name
if not path.is_file():
raise ValueError(f"일반 파일이 아닙니다: {path}")
stat = path.stat()
rows.append({
"relative_path": path.relative_to(folder).as_posix(),
"extension": path.suffix.lower() or "(없음)",
"size_bytes": stat.st_size,
"modified_utc": datetime.fromtimestamp(
stat.st_mtime, timezone.utc
).isoformat(timespec="seconds"),
})
return sorted(rows, key=lambda row: row["relative_path"])
def main():
if OUTPUT.exists() or OUTPUT.is_symlink():
raise FileExistsError("outputs가 이미 있습니다. 기존 결과를 옮긴 뒤 실행하세요.")
rows = collect_files(INPUT) # 모두 읽은 뒤에만 출력 폴더를 만듭니다.
OUTPUT.mkdir()
target = OUTPUT / "file_list.csv"
# x는 기존 파일을 덮어쓰지 않는 모드입니다. BOM은 한글 CSV 열기에 도움을 줍니다.
with target.open("x", encoding="utf-8-sig", newline="") as stream:
writer = csv.DictWriter(stream, fieldnames=FIELDS)
writer.writeheader()
writer.writerows(rows)
print(f"완료: {len(rows)}개 파일, 합계 {sum(row['size_bytes'] for row in rows)}바이트")
print(target)
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except (OSError, ValueError) as error:
print(f"중지: {error}", file=sys.stderr)
raise SystemExit(2)
csv.DictWriter がCSVの区切り文字と引用符処理を行います。ファイルは newline を空文字列に設定し、UTF-8 BOM付きで保存されます。出力ファイルは x モードで開かれるため、すでに存在するファイルを上書きしません。この例では、まず outputs フォルダー自体が存在するかどうかを確認します。
| relative_path | extension | size_bytes |
|---|---|---|
| docs/agenda.txt | .txt | 15 |
| empty.txt | .txt | 0 |
| readme.txt | .txt | 17 |
| reports/sales.csv | .csv | 31 |
表の合計は 15 + 0 + 17 + 31 = 63 バイトです。ターミナルにも “완료: 4개 파일, 합계 63바이트”(完了: 4ファイル、合計63バイト)と表示されます。CSVの1行目には列名が入るため、表計算ソフトではヘッダーを含めて5行表示されます。empty.txt を数え忘れないでください。
失敗条件も設計の一部です。再実行を妨げるからといって、出力保護コードを削除しないでください。以前の結果を残すには、再実行前に outputs を日付を含む名前へ変更するか、ZIPを新しいフォルダーに展開して新しいテストを開始します。
最初から作業フォルダー全体をこの例の対象にしないでください。sample_files のコピーに、代表的なファイルを数個入れて試します。Path オブジェクトは、韓国語文字や空白を含むファイル名も扱えます。別の場所を指すシンボリックリンクやディレクトリジャンクションは対応範囲外なので、通常のファイルだけを含む小さなコピーを使用してください。
入力フォルダーが空の場合、スクリプトはヘッダーだけのCSVを作成し、ファイル数0と報告します。これはフォルダー自体が存在せず停止する場合とは異なります。空のフォルダーも有効な作業状態になり得るため、結果を解釈するときは両者を区別してください。
| 症状 | 考えられる原因 | 次にすること |
|---|---|---|
| python command not found | PythonランチャーのインストールまたはPATH設定の問題 | まず python --version を確認し、Windowsでは py も試します。 |
| sample_files must be a real folder | フォルダーが存在しない、またはリンクが使用されている | ZIP全体を展開したことを確認し、実体のあるフォルダーを復元します。 |
| outputs already exists | 以前の実行結果がまだ残っている | 既存の結果を安全な場所へ移動してから再実行します。 |
| Korean text appears garbled | ファイルを開くプログラムが文字エンコーディングを誤って解釈している | CSVをインポートするときにUTF-8を選択し、元ファイルは変更しないでください。 |
| Read permission error | アクセスできないフォルダーまたはロックされたパス | まずアクセス可能な練習用コピーに対象を絞ります。 |
結果には実行時に観測されたファイル情報が含まれます。これは重複内容の検査でも、復元可能なバックアップでもありません。同じファイルサイズでも、2つの文書の内容が異なることがあります。ハッシュ比較が必要な場合は、別の作業として定義し、必要な列を追加してください。
この初心者向けの例では、走査中に別のプログラムが名前や内容を変更する場合、ネットワークドライブの遅延、非常に多数のファイルをメモリに収集する場合は扱っていません。ディスク書き込みに失敗すると不完全な outputs フォルダーが残ることがあるため、完了メッセージと結果行数の両方を確認してください。
2026-09-19 · Windows 11 · CPython 3.12.14 · 追加パッケージなし · 配布物の一時コピーで実行
コード、入力データ、実行手順が含まれています。展開して、まずREADME.txtを読んでください。
サンプルZIPをダウンロードサンプルコード、ファイル名、入力キーは原文のままです。翻訳本文のコマンドと確認手順も併せて参照してください。
独自に作成した練習用資料 · 元のファイルを別に保管してから実行してください。
説明と例は独自に作成しました。関連する動作や概念は、以下の公式資料で確認できます。