フォルダー内のファイル一覧をCSVで作成する
サブフォルダーを走査し、ファイルパス、拡張子、サイズ、更新日時を表として記録します。元のファイルと既存の結果をそのまま保持したまま、まず4つの小さなサンプルファイルで確認します。
欠落したキー、null 値、空文字列を区別しながらデータを表形式に変換し、別の問題レポートを作成します。
この翻訳はAIで作成しました。コード、単位、数値は原文と併せて確認してください。各言語のネイティブ話者による校閲は、まだ完了していません。 English
対象読者JSON ファイルを表形式で確認し、どこにデータ欠損があるかを見つけたい Python 初学者
JSON では、キーの欠落、null 値、空文字列はそれぞれ異なる状態です。CSV ではすべて空白セルのように見える可能性があるため、この例では元の状態を別の field_issues.csv ファイルに記録します。任意の置換値を自動で埋めることはありません。
想定する列は item, quantity, unit です。値 0、false、半角スペース1文字は欠損として扱いません。コードはまずキーが存在するか確認し、その後で値の状態を確認します。
[
{"item": "가상부품-A", "quantity": 3, "unit": "개"},
{"item": "가상부품-B", "quantity": 5},
{"item": "가상부품-C", "unit": "개"},
{"item": "가상부품-D", "quantity": null, "unit": ""}
]レコードは4件あります。2件目は unit キーがなく、3件目は quantity キーがありません。4件目では quantity が null、unit が空文字列です。item 名と unit は実際の入力値なので、翻訳版でもそのまま保持します。
python --version
python example.py sample.json --output-dir outputsコマンド内の相対パスは、ターミナルの現在のフォルダを基準に解決されます。実行前に example.py があるフォルダへ移動してください。出力フォルダはまだ存在していてはいけません。また、その親フォルダは事前に存在している必要があります。outputs がすでに存在する場合は、新しい名前を選んでください。
python example.py sample.json --output-dir outputs_second入力の検証が完了した後に新しいフォルダが作成されます。コードは既存の出力フォルダを空にしたり上書きしたりせず、入力 JSON も変更しません。
| ファイル | 確認すること |
|---|---|
| converted.csv | ヘッダーの後にデータ行が4件 |
| field_issues.csv | missing_key: 2, null: 1, empty_string: 1 |
| summary.json | input_rows=4, output_rows=4, reported_issue_cells=4 |
OK: rows=4; missing_key_cells=2; null_cells=1; empty_string_cells=1; issue_cells=4問題件数は、問題のある行数ではなく、問題のあるセル数を指します。4行目には問題が2つあるため、報告される問題の合計は4件になります。まず CSV をテキストエディタで確認してください。任意の外部データは表計算ソフトによって自動解釈される可能性があり、このコードは formula strings を無害化しません。
JSON array 内の objects だけを許可します。nested objects または arrays、未知の columns、duplicate JSON keys、NaN、Infinity は拒否されます。不要な binary floating point 変換を避けるため、decimal numbers は Decimal として読み取ります。5 MiB を超える入力ファイルは処理しません。
#!/usr/bin/env python3
"""Convert a small local JSON array to CSV and a field-issue report.
Target: Python 3.12+, standard library only. No network access.
All output goes to a NEW directory; existing directories/files are refused.
"""
from __future__ import annotations
import argparse
import csv
import io
import json
import sys
from decimal import Decimal
from pathlib import Path
DEFAULT_FIELDS = ("item", "quantity", "unit")
MAX_INPUT_BYTES = 5 * 1024 * 1024
ISSUE_FIELDS = ("row_number", "field", "issue")
class InputError(ValueError):
"""The input does not satisfy this example's data contract."""
def unique_object(pairs: list[tuple[str, object]]) -> dict[str, object]:
result: dict[str, object] = {}
for key, value in pairs:
if key in result:
raise InputError("Duplicate key in a JSON object.")
result[key] = value
return result
def reject_constant(token: str) -> object:
raise InputError(f"Non-standard JSON number: {token}.")
def csv_bytes(fields: tuple[str, ...], rows: list[dict]) -> bytes:
buffer = io.StringIO(newline="")
writer = csv.DictWriter(buffer, fieldnames=fields, lineterminator="\r\n")
writer.writeheader()
writer.writerows(rows)
return buffer.getvalue().encode("utf-8")
def prepare_outputs(source: Path, fields: tuple[str, ...]) -> tuple[dict, dict]:
if not fields or len(fields) != len(set(fields)):
raise InputError("--fields must contain at least one unique field name.")
if any(not field.strip() for field in fields):
raise InputError("Field names must not be empty or whitespace-only.")
# Read-only, bounded input. Do not modify the source, even on failure.
with source.open("rb") as stream:
raw = stream.read(MAX_INPUT_BYTES + 1)
if len(raw) > MAX_INPUT_BYTES:
raise InputError("Input exceeds the example's 5 MiB limit.")
records = json.loads(
raw.decode("utf-8-sig"),
object_pairs_hook=unique_object,
parse_float=Decimal,
parse_constant=reject_constant,
)
if not isinstance(records, list):
raise InputError("Top-level JSON value must be an array.")
rows: list[dict[str, str]] = []
issues: list[dict[str, object]] = []
counts = {"missing_key": 0, "null": 0, "empty_string": 0}
missing_rows: set[int] = set()
allowed = set(fields)
for row_number, record in enumerate(records, start=1):
if not isinstance(record, dict):
raise InputError(f"Record {row_number} must be an object.")
if set(record) - allowed:
raise InputError(
f"Record {row_number} has unlisted keys; include them in --fields."
)
row: dict[str, str] = {}
for field in fields:
issue = None
if field not in record:
issue = "missing_key"
missing_rows.add(row_number)
value = None
else:
value = record[field]
if value is None:
issue = "null"
elif value == "":
issue = "empty_string"
if issue is not None:
counts[issue] += 1
issues.append({"row_number": row_number, "field": field, "issue": issue})
row[field] = ""
elif isinstance(value, str):
row[field] = value
elif isinstance(value, bool):
row[field] = "true" if value else "false"
elif isinstance(value, (int, Decimal)):
row[field] = str(value)
else:
raise InputError(
f"Record {row_number} contains an unsupported nested value."
)
rows.append(row)
summary = {
"status": "complete",
"columns": list(fields),
"input_rows": len(records),
"output_rows": len(rows),
"missing_key_cells": counts["missing_key"],
"rows_with_missing_keys": len(missing_rows),
"null_cells": counts["null"],
"empty_string_cells": counts["empty_string"],
"reported_issue_cells": len(issues),
}
# Render and UTF-8-encode EVERYTHING before creating the output directory.
# summary.json is written last and serves as a completion record.
outputs = {
"converted.csv": csv_bytes(fields, rows),
"field_issues.csv": csv_bytes(ISSUE_FIELDS, issues),
"summary.json": (json.dumps(summary, ensure_ascii=False, indent=2) + "\n").encode("utf-8"),
}
return outputs, summary
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("input", type=Path, help="Local UTF-8 JSON file")
parser.add_argument("--output-dir", type=Path, required=True, help="NEW directory; parent must exist")
parser.add_argument("--fields", nargs="+", default=list(DEFAULT_FIELDS), help="Expected CSV columns, in order")
args = parser.parse_args(argv)
if sys.version_info < (3, 12):
print("E_VERSION: Python 3.12 or newer is required.", file=sys.stderr)
return 2
try:
outputs, summary = prepare_outputs(args.input, tuple(args.fields))
except json.JSONDecodeError as exc:
print(f"E_INPUT: Invalid JSON at line {exc.lineno}, column {exc.colno}.", file=sys.stderr)
return 2
except (ValueError, ArithmeticError, RecursionError) as exc:
print(f"E_INPUT: {exc}", file=sys.stderr)
return 2
except OSError as exc:
print(f"E_READ: Cannot read input ({exc.__class__.__name__}).", file=sys.stderr)
return 4
try:
args.output_dir.mkdir(exist_ok=False)
except FileExistsError:
print("E_OUTPUT_EXISTS: Output path already exists; choose a new directory.", file=sys.stderr)
return 3
except OSError as exc:
print(f"E_WRITE: Cannot create output directory ({exc.__class__.__name__}).", file=sys.stderr)
return 4
try:
for name, payload in outputs.items():
with (args.output_dir / name).open("xb") as stream:
stream.write(payload)
except OSError as exc:
print(
f"E_WRITE: Incomplete NEW output directory ({exc.__class__.__name__}); "
"do not use its results. Inspect it and choose a new directory.",
file=sys.stderr,
)
return 4
print(
f"OK: rows={summary['output_rows']}; "
f"missing_key_cells={summary['missing_key_cells']}; "
f"null_cells={summary['null_cells']}; "
f"empty_string_cells={summary['empty_string_cells']}; "
f"issue_cells={summary['reported_issue_cells']}"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
python -B -X utf8 -m unittest -v test_exampleテストでは temporary folders を使い、通常の変換、欠落値と空値の区別、不正な入力、既存出力の保護を確認します。22 tests の最終結果が OK であることを確認してください。テストに合格しても、すべての実データ形式をサポートしていることを意味するわけではありません。
| メッセージ | 確認すること |
|---|---|
| E_INPUT | JSON syntax、column names、value types、input size を確認します。 |
| E_OUTPUT_EXISTS | 新しい output folder name を指定します。 |
| E_READ | input path と read permissions を確認します。 |
| E_WRITE | write permissions と disk の状態を確認します。不完全な output は使用しないでください。 |
この例は小規模ファイル向けで、入力と出力の内容全体をメモリに保持します。nested JSON の flatten、date conversion、column の自動推論、missing values の補完は行いません。エラーを修正する場合は、元ファイルの練習用コピーで作業してください。
出力書き込み中に disk error が発生すると、一部のファイルだけが残る可能性があります。success message と3つすべてのファイル内容の両方を確認してください。この例では、CSV ファイルを開いたときの表計算ソフトによる自動変換動作は検証していません。
2026-09-19 · Windows 11 · CPython 3.12.14 · 標準ライブラリ
コード、入力データ、実行手順が含まれています。展開して、まずREADME.txtを読んでください。
サンプルZIPをダウンロードサンプルコード、ファイル名、入力キーは原文のままです。翻訳本文のコマンドと確認手順も併せて参照してください。
独自に作成した練習用資料 · 元のファイルを別に保管してから実行してください。
説明と例は独自に作成しました。関連する動作や概念は、以下の公式資料で確認できます。