Python入門:ファイル操作(読み書き)

2025-10-24

はじめに

前回の講義ではリスト、タプル、辞書、集合について学びました。今回は、プログラムの実行結果を永続化したり、外部データを読み込んだりするために不可欠な「ファイル操作」について学びます。ファイル操作をマスターすることで、データの保存・読み込みが可能になり、より実用的なプログラムを作成できるようになります。

ファイル操作の重要性

ファイル操作は、プログラムと外部世界をつなぐ重要な橋渡し役です。

  • 設定ファイルの読み書き
  • データの永続的な保存
  • ログファイルの作成
  • 外部データのインポート/エクスポート
  • 大規模データの処理

ファイル操作の基本

ファイルのオープンとクローズ

Pythonでファイルを操作する基本的な流れは「オープン → 操作 → クローズ」です。

open() でファイルを開き、read() で内容を読み込み、最後に close() でファイルを閉じています。
try-finally 構文を使うことで、処理中にエラーが発生しても必ず file.close() が実行され、
リソースリーク(ファイルを閉じ忘れる問題)を防ぐ安全な構成になっています。

# 基本的なファイル操作
try:
    # ファイルをオープン(読み込みモード)
    file = open('sample.txt', 'r', encoding='utf-8')

    # ファイルの操作
    content = file.read()
    print(content)

finally:
    # ファイルをクローズ
    file.close()

with文を使った安全なファイル操作

with文を使用すると、ファイルのクローズを自動で行ってくれます。with open(...) as file: と書くことで、ブロック内の処理が終わると自動的にファイルが閉じられます。

これにより、close() を明示的に書く必要がなく、エラーが起きても安全にリソースを解放できます。
可読性が高く、推奨されるファイル操作の方法です。

# with文を使った安全なファイル操作
with open('sample.txt', 'r', encoding='utf-8') as file:
    content = file.read()
    print(content)
# ここで自動的にファイルがクローズされる

ファイルの読み込み

さまざまな読み込み方法

sample.txtを作成し、read()で全体を一括取得、readline()で1行ずつ、readlines()で行リストとして取得、さらにファイルオブジェクトを直接反復処理し、最後に指定バイト数のみを読み込む手法を順に実演しています。

def demonstrate_reading():
    """さまざまなファイル読み込み方法を演示"""

    # サンプルファイルを作成
    sample_content = """これは1行目です。
これは2行目です。
これは3行目です。
4行目には数字123が含まれています。
5行目は最後の行です。"""

    with open('sample.txt', 'w', encoding='utf-8') as f:
        f.write(sample_content)

    print("=== ファイル読み込みのデモ ===")

    # 1. read() - ファイル全体を一括読み込み
    with open('sample.txt', 'r', encoding='utf-8') as f:
        content = f.read()
        print("1. read() メソッド:")
        print(content)
        print("-" * 40)

    # 2. readline() - 1行ずつ読み込み
    with open('sample.txt', 'r', encoding='utf-8') as f:
        print("2. readline() メソッド:")
        line1 = f.readline()
        line2 = f.readline()
        print(f"1行目: {line1.strip()}")
        print(f"2行目: {line2.strip()}")
        print("-" * 40)

    # 3. readlines() - すべての行をリストで読み込み
    with open('sample.txt', 'r', encoding='utf-8') as f:
        lines = f.readlines()
        print("3. readlines() メソッド:")
        for i, line in enumerate(lines, 1):
            print(f"行{i}: {line.strip()}")
        print("-" * 40)

    # 4. ファイルオブジェクトを直接イテレート
    with open('sample.txt', 'r', encoding='utf-8') as f:
        print("4. ファイルオブジェクトのイテレーション:")
        for i, line in enumerate(f, 1):
            print(f"行{i}: {line.strip()}")
        print("-" * 40)

    # 5. 特定のバイト数だけ読み込み
    with open('sample.txt', 'r', encoding='utf-8') as f:
        print("5. 特定バイト数の読み込み:")
        first_10_bytes = f.read(10)
        print(f"最初の10バイト: {first_10_bytes}")
        print("-" * 40)

# デモの実行
demonstrate_reading()

大きなファイルの効率的な読み込み

open()を使ってファイルを1行ずつ読み込み、行数・単語数・文字数をカウントします。1000行ごとに進捗を表示し、最後に統計結果を出力します。メモリを無駄に使わず、大規模データ処理に適した構成です。

def process_large_file(filename):
    """大きなファイルをメモリ効率的に処理する"""
    try:
        with open(filename, 'r', encoding='utf-8') as file:
            line_count = 0
            word_count = 0
            char_count = 0

            for line in file:
                line_count += 1
                word_count += len(line.split())
                char_count += len(line)

                # 1000行ごとに進捗を表示
                if line_count % 1000 == 0:
                    print(f"処理中... {line_count}行")

            print(f"ファイル統計:")
            print(f"  行数: {line_count}")
            print(f"  単語数: {word_count}")
            print(f"  文字数: {char_count}")

    except FileNotFoundError:
        print(f"ファイル '{filename}' が見つかりません")
    except IOError as e:
        print(f"ファイル読み込みエラー: {e}")

# 使用例
# process_large_file('large_data.txt')

ファイルの書き込み

さまざまな書き込み方法

write()で単一文字列を、writelines()で複数行リストを出力し、aモードで追記操作も紹介します。さらに辞書データをCSV形式に整形して書き込み、最後に作成した各ファイルを読み出して内容を確認します。ファイル出力の基本と実用例を総合的に学べる構成です。

def demonstrate_writing():
    """さまざまなファイル書き込み方法を演示"""

    print("=== ファイル書き込みのデモ ===")

    # 1. write() - 文字列を書き込み
    with open('output1.txt', 'w', encoding='utf-8') as f:
        f.write("これはwrite()メソッドで書き込まれたテキストです。\n")
        f.write("2行目を追加します。\n")
        print("1. write()メソッドでファイルを作成しました")

    # 2. writelines() - 文字列のリストを書き込み
    lines = [
        "これはwritelines()で書き込まれたテキストです。\n",
        "リストの各要素が1行になります。\n",
        "最後の行です。\n"
    ]

    with open('output2.txt', 'w', encoding='utf-8') as f:
        f.writelines(lines)
        print("2. writelines()メソッドでファイルを作成しました")

    # 3. 追記モードでの書き込み
    with open('output3.txt', 'w', encoding='utf-8') as f:
        f.write("最初の内容\n")

    with open('output3.txt', 'a', encoding='utf-8') as f:  # 'a'は追記モード
        f.write("追記された内容\n")
        f.write("さらに追記\n")
        print("3. 追記モードでファイルを更新しました")

    # 4. フォーマット済み文字列の書き込み
    students = [
        {"name": "山田太郎", "score": 85},
        {"name": "佐藤花子", "score": 92},
        {"name": "鈴木一郎", "score": 78}
    ]

    with open('students.txt', 'w', encoding='utf-8') as f:
        f.write("名前,点数\n")  # ヘッダー行
        for student in students:
            line = f"{student['name']},{student['score']}\n"
            f.write(line)
        print("4. CSV形式でデータを書き込みました")

    # 作成されたファイルの内容を確認
    print("\n作成されたファイルの内容:")
    for filename in ['output1.txt', 'output2.txt', 'output3.txt', 'students.txt']:
        print(f"\n{filename}:")
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                print(f.read())
        except FileNotFoundError:
            print("ファイルが見つかりません")

# デモの実行
demonstrate_writing()

ファイルモードの詳細

主要なファイルモード

open()関数の第2引数で指定し、ファイル操作の目的を決定します。主なものに、読み込み専用の'r'、書き込み専用(既存内容を上書き)の'w'、追記用の'a'、読み書き両用の'r+'、新規作成専用の'x'があります。また、バイナリ操作では'rb''wb'のようにbを付けて使用します。

モード説明ファイルが存在する場合ファイルが存在しない場合
'r'読み込み(デフォルト)読み込み可エラー
'w'書き込み上書き新規作成
'a'追記末尾に追加新規作成
'r+'読み込み+書き込み読み書き可エラー
'w+'書き込み+読み込み上書き新規作成
'a+'追記+読み込み読み込みと追記新規作成
'x'排他的作成エラー新規作成

バイナリモード

テキストをUTF-8でエンコードしてバイナリ形式で保存し、再度デコードして読み出す処理を示します。さらに、bytes()で作成したバイト配列(例:A〜E)をファイルに保存し、読み込んでバイト列および文字列として表示します。バイナリ入出力の基本理解に役立つ構成です。

def binary_file_operations():
    """バイナリファイルの操作"""

    # テキストデータをバイナリで書き込み
    text_data = "これはバイナリで保存されるテキストです"

    with open('binary_text.bin', 'wb') as f:  # 'wb' = バイナリ書き込みモード
        f.write(text_data.encode('utf-8'))
        print("テキストをバイナリファイルとして保存しました")

    # バイナリファイルの読み込み
    with open('binary_text.bin', 'rb') as f:  # 'rb' = バイナリ読み込みモード
        binary_content = f.read()
        text_content = binary_content.decode('utf-8')
        print(f"バイナリファイルから読み込んだテキスト: {text_content}")

    # バイトデータの操作
    byte_data = bytes([65, 66, 67, 68, 69])  # ABCDE

    with open('bytes_data.bin', 'wb') as f:
        f.write(byte_data)
        print("バイトデータをファイルに保存しました")

    with open('bytes_data.bin', 'rb') as f:
        read_bytes = f.read()
        print(f"読み込んだバイトデータ: {list(read_bytes)}")
        print(f"文字列として: {read_bytes.decode('ascii')}")

# バイナリ操作のデモ
binary_file_operations()

Pythonのファイル操作ベストプラクティス

CSVファイルの操作

csvモジュールを使ってCSVファイルを読み書きする方法を実演しています。writer.writerows()で学生データを表形式に書き込み、次にcsv.reader()でリスト形式として読み出します。さらにcsv.DictReader()を用いて辞書形式で各行のデータを扱い、キー名で値を取得します。CSV操作の基本を網羅した実践的な例です。

import csv

def csv_operations():
    """CSVファイルの読み書き"""

    # CSVファイルの書き込み
    students = [
        ['名前', '年齢', '点数', 'クラス'],
        ['山田太郎', 18, 85, 'A'],
        ['佐藤花子', 17, 92, 'B'],
        ['鈴木一郎', 18, 78, 'A'],
        ['田中美咲', 17, 88, 'B']
    ]

    # CSVモジュールを使用した書き込み
    with open('students.csv', 'w', encoding='utf-8', newline='') as f:
        writer = csv.writer(f)
        writer.writerows(students)
        print("CSVファイルを作成しました")

    # CSVファイルの読み込み
    print("\nCSVファイルの内容:")
    with open('students.csv', 'r', encoding='utf-8') as f:
        reader = csv.reader(f)
        for row in reader:
            print(row)

    # 辞書形式でのCSV操作
    print("\n辞書形式でのCSV操作:")
    with open('students.csv', 'r', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            print(f"名前: {row['名前']}, 年齢: {row['年齢']}, 点数: {row['点数']}")

# CSV操作のデモ
csv_operations()

設定ファイルの管理

jsonモジュールを用いて設定ファイルを操作する例です。辞書形式の設定データをjson.dump()で整形してconfig.jsonに保存し、json.load()で読み込み内容を確認します。その後、テーマ設定を変更し、最近のファイルに新しい項目を追加して更新版を再保存します。設定管理やアプリ構成ファイルの扱いに役立つ実践的なサンプルです。

import json

def config_file_operations():
    """JSON設定ファイルの操作"""

    # 設定データ
    config_data = {
        "app_name": "My Application",
        "version": "1.0.0",
        "settings": {
            "theme": "dark",
            "language": "ja",
            "auto_save": True
        },
        "recent_files": [
            "document1.txt",
            "document2.txt"
        ]
    }

    # JSONファイルへの書き込み
    with open('config.json', 'w', encoding='utf-8') as f:
        json.dump(config_data, f, ensure_ascii=False, indent=2)
        print("設定ファイルを保存しました")

    # JSONファイルからの読み込み
    with open('config.json', 'r', encoding='utf-8') as f:
        loaded_config = json.load(f)
        print("設定ファイルを読み込みました:")
        print(json.dumps(loaded_config, ensure_ascii=False, indent=2))

    # 設定の更新
    loaded_config['settings']['theme'] = 'light'
    loaded_config['recent_files'].append('document3.txt')

    with open('config_updated.json', 'w', encoding='utf-8') as f:
        json.dump(loaded_config, f, ensure_ascii=False, indent=2)
        print("更新された設定ファイルを保存しました")

# 設定ファイル操作のデモ
config_file_operations()

ログファイルの作成

write_log()関数で現在時刻とログレベル(INFOやERRORなど)を付与してapp.logに追記し、同時にコンソールにも出力します。起動、ログイン、エラー、完了といったイベントを記録し、最後にログファイル全体を読み込んで表示します。実用的な基本構造を持つログ処理の入門例です。

import datetime

def create_log_system():
    """シンプルなログシステム"""

    def write_log(message, level="INFO"):
        """ログをファイルに書き込む"""
        timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        log_entry = f"[{timestamp}] [{level}] {message}\n"

        with open('app.log', 'a', encoding='utf-8') as f:
            f.write(log_entry)

        print(log_entry.strip())  # コンソールにも表示

    # ログの使用例
    write_log("アプリケーションを起動しました")
    write_log("ユーザーログイン: alice", "INFO")
    write_log("データベース接続エラー", "ERROR")
    write_log("処理が完了しました", "INFO")

    # ログファイルの内容を表示
    print("\nログファイルの内容:")
    try:
        with open('app.log', 'r', encoding='utf-8') as f:
            print(f.read())
    except FileNotFoundError:
        print("ログファイルが見つかりません")

# ログシステムのデモ
create_log_system()

osモジュールを使った操作

osshutilモジュールを使い、現在の作業ディレクトリ取得、ファイル存在確認、ディレクトリ作成、ファイルコピー・リネーム・削除、一覧取得、ディレクトリ削除といった一連の処理を行います。実際のファイル管理やバックアップ処理の基礎を学ぶのに最適な例です。

import os
import shutil

def file_system_operations():
    """ファイルシステムの操作"""

    print("=== ファイルシステム操作 ===")

    # カレントディレクトリの取得
    current_dir = os.getcwd()
    print(f"現在のディレクトリ: {current_dir}")

    # ファイルの存在確認
    files_to_check = ['sample.txt', 'nonexistent.txt', 'students.csv']
    for filename in files_to_check:
        exists = os.path.exists(filename)
        print(f"ファイル '{filename}' の存在: {exists}")

    # ディレクトリの作成
    if not os.path.exists('test_dir'):
        os.makedirs('test_dir')
        print("ディレクトリ 'test_dir' を作成しました")

    # ファイルのコピー
    if os.path.exists('sample.txt'):
        shutil.copy('sample.txt', 'test_dir/copied_sample.txt')
        print("ファイルをコピーしました")

    # ファイルのリネーム
    if os.path.exists('test_dir/copied_sample.txt'):
        os.rename('test_dir/copied_sample.txt', 'test_dir/renamed_sample.txt')
        print("ファイルをリネームしました")

    # ディレクトリ内のファイル一覧
    print("\nディレクトリ内のファイル:")
    for item in os.listdir('.'):
        if os.path.isfile(item):
            item_type = "ファイル"
        elif os.path.isdir(item):
            item_type = "ディレクトリ"
        else:
            item_type = "その他"

        size = os.path.getsize(item)
        print(f"  {item} ({item_type}, {size} バイト)")

    # ファイルの削除
    if os.path.exists('test_dir/renamed_sample.txt'):
        os.remove('test_dir/renamed_sample.txt')
        print("ファイルを削除しました")

    # ディレクトリの削除
    if os.path.exists('test_dir'):
        os.rmdir('test_dir')
        print("ディレクトリを削除しました")

# ファイルシステム操作のデモ
file_system_operations()

堅牢なファイル操作

例外処理を活用して安全にファイルを読み書きする方法を示しています。safe_read_file()はファイルの存在・権限・文字コード・入出力エラーを個別に捕捉し、問題発生時に詳細なエラーメッセージを表示します。safe_write_file()も同様に権限やI/Oエラーを検知し、安全に書き込みを行います。堅牢で信頼性の高いファイル操作を行うための実践的サンプルです。

def robust_file_operations():
    """エラーハンドリングを含む堅牢なファイル操作"""

    def safe_read_file(filename):
        """安全なファイル読み込み"""
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                return f.read()
        except FileNotFoundError:
            print(f"エラー: ファイル '{filename}' が見つかりません")
            return None
        except PermissionError:
            print(f"エラー: ファイル '{filename}' の読み込み権限がありません")
            return None
        except UnicodeDecodeError:
            print(f"エラー: ファイル '{filename}' の文字コードが正しくありません")
            return None
        except IOError as e:
            print(f"エラー: ファイル操作中に問題が発生しました - {e}")
            return None

    def safe_write_file(filename, content):
        """安全なファイル書き込み"""
        try:
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(content)
            print(f"ファイル '{filename}' への書き込みが成功しました")
            return True
        except PermissionError:
            print(f"エラー: ファイル '{filename}' への書き込み権限がありません")
            return False
        except IOError as e:
            print(f"エラー: ファイル書き込み中に問題が発生しました - {e}")
            return False

    # 安全なファイル操作の使用例
    content = safe_read_file('sample.txt')
    if content:
        print("ファイルの内容:")
        print(content)

    # 新しい内容の書き込み
    new_content = "これは安全なファイル操作で書き込まれた内容です"
    success = safe_write_file('safe_output.txt', new_content)

    if success:
        # 書き込んだ内容を確認
        verified_content = safe_read_file('safe_output.txt')
        if verified_content:
            print("書き込まれた内容を確認:")
            print(verified_content)

# 堅牢なファイル操作のデモ
robust_file_operations()

大きなファイルの処理

大容量ファイルを効率よく処理するための実践例です。process_large_file_in_chunks()では一定サイズ(チャンク)ごとに読み込み、逐次的に行を処理してメモリ使用量を抑えます。memory_efficient_search()はファイルを1行ずつ走査して特定語句を検索し、ヒットした行番号と内容を記録します。大量データ処理やログ解析などに有効な、メモリ効率を重視した設計です。

def efficient_large_file_processing():
    """大きなファイルを効率的に処理する"""

    def process_large_file_in_chunks(filename, chunk_size=8192):
        """チャンク単位で大きなファイルを処理する"""
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                buffer = ""
                chunk_count = 0
                line_count = 0

                while True:
                    chunk = f.read(chunk_size)
                    if not chunk:
                        break

                    chunk_count += 1
                    buffer += chunk

                    # バッファから行を処理
                    while '\n' in buffer:
                        line, buffer = buffer.split('\n', 1)
                        line_count += 1

                        # 行の処理(ここでは単純にカウント)
                        if line_count % 1000 == 0:
                            print(f"処理済み: {line_count}行")

                # 最後のバッファ内容を処理
                if buffer:
                    line_count += 1

                print(f"処理完了: {line_count}行, {chunk_count}チャンク")

        except FileNotFoundError:
            print(f"ファイル '{filename}' が見つかりません")

    def memory_efficient_search(filename, search_term):
        """メモリ効率的なファイル検索"""
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                matches = []
                line_number = 0

                for line in f:
                    line_number += 1
                    if search_term in line:
                        matches.append((line_number, line.strip()))

                return matches

        except FileNotFoundError:
            print(f"ファイル '{filename}' が見つかりません")
            return []

    # 大きなファイルを作成(デモ用)
    with open('large_demo.txt', 'w', encoding='utf-8') as f:
        for i in range(10000):
            f.write(f"これは行 {i+1} です。サンプルテキストが含まれています。\n")

    print("大きなファイルを処理中...")
    process_large_file_in_chunks('large_demo.txt')

    print("\nファイル内の検索:")
    results = memory_efficient_search('large_demo.txt', 'サンプル')
    print(f"検索結果: {len(results)}件見つかりました")
    if results:
        print("最初の5件:")
        for line_num, line in results[:5]:
            print(f"  行{line_num}: {line}")

# 効率的なファイル処理のデモ
efficient_large_file_processing()

まとめ

この講義では、Pythonのファイル操作について学びました。ファイルの開閉や操作の基本的な流れを理解し、with 文を用いた安全で自動的にクローズされるファイル操作方法を習得しました。さらに、read()readline()readlines() を使った読み込み、write()writelines() による書き込み、追記モードなどの使い分けも学びました。

また、CSV・JSON・ログなどの実践的なファイル処理や、ファイルやディレクトリを扱うファイルシステム操作、例外処理による堅牢な設計、そして大規模ファイルを効率的に扱うためのパフォーマンス最適化についても理解を深めました。

ファイル操作は、データの永続化や外部システムとの連携に不可欠なスキルです。適切なエラーハンドリングと効率的な処理方法を理解することで、より信頼性の高いプログラムを作成できるようになります。


演習問題

初級問題(3問)

問題1
テキストファイルを作成し、以下の内容を書き込むプログラムを作成してください。

1行目: Pythonの学習
2行目: ファイル操作の練習
3行目: 読み書きの基本

その後、同じファイルを読み込んで内容を表示してください。

問題2
ユーザーから入力された文字列をファイルに追記するプログラムを作成してください。プログラムはユーザーが「終了」と入力するまで続き、最後にファイルの全内容を表示してください。

問題3
既存のテキストファイルの行数をカウントするプログラムを作成してください。ファイルが存在しない場合はエラーメッセージを表示してください。

中級問題(6問)

問題4
CSVファイルを作成し、以下の学生データを書き込むプログラムを作成してください。

名前,科目,点数
山田太郎,数学,85
佐藤花子,英語,92
鈴木一郎,科学,78

その後、このCSVファイルを読み込んで各学生のデータを表示してください。

問題5
設定情報をJSONファイルに保存し、読み込むプログラムを作成してください。設定にはアプリケーション名、バージョン、作者情報を含めてください。

問題6
ログファイルを作成するプログラムを作成してください。プログラムの実行時間とともにINFO、WARNING、ERRORレベルのログを記録できるようにしてください。

問題7
2つのテキストファイルを比較し、同じ行と異なる行を別々のファイルに出力するプログラムを作成してください。

問題8
特定のディレクトリ内のすべてのテキストファイル(.txt)を検索し、ファイル名とサイズのリストをCSVファイルに出力するプログラムを作成してください。

問題9
大きなテキストファイルから特定のキーワードを含む行を抽出して新しいファイルに保存するプログラムを作成してください。メモリ効率を考慮した実装にしてください。

上級問題(3問)

問題10
簡単なデータベースのような機能を持つプログラムを作成してください。

  • ユーザー情報(名前、年齢、メール)をCSVファイルに追加
  • 全ユーザーの一覧表示
  • 特定のユーザーの検索
  • ユーザー情報の更新
  • ユーザーの削除

問題11
ファイルのバックアップシステムを作成してください。

  • 指定したディレクトリ内のファイルをバックアップ
  • バックアップ日時をファイル名に含める
  • 既存のバックアップとの比較
  • 増分バックアップの機能

問題12
マークダウンファイルを処理するプログラムを作成してください。

  • 見出し(#)の階層を解析
  • コードブロック(“`)を抽出
  • リンクと画像の情報を収集
  • 解析結果を構造化してJSONで出力

初級問題 解答

問題1 解答

def create_and_read_file():
    """テキストファイルの作成と読み込み"""

    # ファイルに書き込み
    content = """1行目: Pythonの学習
2行目: ファイル操作の練習
3行目: 読み書きの基本"""

    try:
        # ファイルへの書き込み
        with open('python_learning.txt', 'w', encoding='utf-8') as file:
            file.write(content)
        print("ファイルを作成しました")

        # ファイルからの読み込み
        with open('python_learning.txt', 'r', encoding='utf-8') as file:
            read_content = file.read()
            print("\nファイルの内容:")
            print(read_content)

        # 行ごとの読み込み(別の方法)
        print("\n行ごとの内容:")
        with open('python_learning.txt', 'r', encoding='utf-8') as file:
            for line_number, line in enumerate(file, 1):
                print(f"{line_number}: {line.strip()}")

    except IOError as e:
        print(f"ファイル操作中にエラーが発生しました: {e}")

# 発展版:ユーザー入力を受け付けるバージョン
def interactive_file_creator():
    """インタラクティブなファイル作成"""

    filename = input("作成するファイル名を入力してください: ")
    lines = []

    print("ファイルの内容を入力してください(空行で終了):")
    while True:
        line = input()
        if line == "":
            break
        lines.append(line)

    # ファイルへの書き込み
    try:
        with open(filename, 'w', encoding='utf-8') as file:
            for line in lines:
                file.write(line + '\n')
        print(f"ファイル '{filename}' を作成しました")

        # 内容の確認
        with open(filename, 'r', encoding='utf-8') as file:
            content = file.read()
            print("\n作成されたファイルの内容:")
            print(content)

    except IOError as e:
        print(f"エラー: {e}")

# メイン実行
if __name__ == "__main__":
    print("=== 問題1 解答 ===")
    create_and_read_file()

    # インタラクティブ版を実行する場合はコメントを外す
    # print("\n=== インタラクティブ版 ===")
    # interactive_file_creator()

実行結果:

=== 問題1 解答 ===
ファイルを作成しました

ファイルの内容:
1行目: Pythonの学習
2行目: ファイル操作の練習
3行目: 読み書きの基本

行ごとの内容:
1: 1行目: Pythonの学習
2: 2行目: ファイル操作の練習
3: 3行目: 読み書きの基本

問題2 解答

def append_to_file():
    """ユーザー入力をファイルに追記"""

    filename = "user_notes.txt"

    print("ファイルへの追記を開始します。'終了'と入力すると終了します。")
    print("-" * 50)

    try:
        # 追記モードでファイルを開く
        with open(filename, 'a', encoding='utf-8') as file:
            while True:
                user_input = input("入力してください: ")

                if user_input.lower() == '終了':
                    print("入力を終了します")
                    break

                # ファイルに追記
                file.write(user_input + '\n')
                print(f"'{user_input}' をファイルに追加しました")

        # ファイルの全内容を表示
        print("\n" + "=" * 50)
        print("ファイルの全内容:")
        print("-" * 50)

        with open(filename, 'r', encoding='utf-8') as file:
            content = file.read()
            if content:
                print(content)
            else:
                print("ファイルは空です")

    except IOError as e:
        print(f"ファイル操作中にエラーが発生しました: {e}")

# 発展版:より多くの機能を持つバージョン
def enhanced_append_system():
    """機能強化版ファイル追記システム"""

    filename = "enhanced_notes.txt"
    entries = []

    print("拡張ファイル追記システム")
    print("利用可能なコマンド:")
    print("  add <テキスト> - テキストを追加")
    print("  list - 現在のエントリを表示")
    print("  save - ファイルに保存")
    print("  clear - エントリをクリア")
    print("  quit - 終了")

    while True:
        command = input("\nコマンドを入力してください: ").strip()

        if command.lower() == 'quit':
            # 終了前に保存
            if entries:
                save = input("保存していないエントリがあります。保存しますか? (y/n): ")
                if save.lower() == 'y':
                    with open(filename, 'a', encoding='utf-8') as file:
                        for entry in entries:
                            file.write(entry + '\n')
                    print("ファイルに保存しました")
            break

        elif command.lower() == 'list':
            if entries:
                print("現在のエントリ:")
                for i, entry in enumerate(entries, 1):
                    print(f"  {i}. {entry}")
            else:
                print("エントリはありません")

        elif command.lower() == 'clear':
            entries.clear()
            print("エントリをクリアしました")

        elif command.lower() == 'save':
            if entries:
                with open(filename, 'a', encoding='utf-8') as file:
                    for entry in entries:
                        file.write(entry + '\n')
                print(f"{len(entries)} 件のエントリをファイルに保存しました")
                entries.clear()
            else:
                print("保存するエントリがありません")

        elif command.startswith('add '):
            text = command[4:].strip()
            if text:
                entries.append(text)
                print(f"エントリを追加しました: '{text}'")
            else:
                print("追加するテキストを入力してください")

        else:
            print("無効なコマンドです")

    # 最終的なファイル内容を表示
    try:
        with open(filename, 'r', encoding='utf-8') as file:
            content = file.read()
            if content:
                print("\n最終的なファイル内容:")
                print("-" * 30)
                print(content)
    except FileNotFoundError:
        print("ファイルは作成されていません")

# メイン実行
if __name__ == "__main__":
    print("=== 問題2 解答 ===")
    append_to_file()

    # 拡張版を実行する場合はコメントを外す
    # print("\n=== 拡張版 ===")
    # enhanced_append_system()

実行結果:

=== 問題2 解答 ===
ファイルへの追記を開始します。’終了’と入力すると終了します。
————————————————–
入力してください: こんにちは
‘こんにちは’ をファイルに追加しました
入力してください: Pythonの学習中です
‘Pythonの学習中です’ をファイルに追加しました
入力してください: ファイル操作は重要です
‘ファイル操作は重要です’ をファイルに追加しました
入力してください: 終了
入力を終了します

==================================================
ファイルの全内容:
————————————————–
こんにちは
Pythonの学習中です
ファイル操作は重要です

問題3 解答

def count_file_lines():
    """ファイルの行数をカウント"""

    filename = input("行数をカウントするファイル名を入力してください: ")

    try:
        line_count = 0
        word_count = 0
        char_count = 0

        with open(filename, 'r', encoding='utf-8') as file:
            for line in file:
                line_count += 1
                word_count += len(line.split())
                char_count += len(line.rstrip('\n'))  # 改行文字を除く

        print(f"\nファイル '{filename}' の統計:")
        print(f"  行数: {line_count}")
        print(f"  単語数: {word_count}")
        print(f"  文字数: {char_count}")

    except FileNotFoundError:
        print(f"エラー: ファイル '{filename}' が見つかりません")
    except PermissionError:
        print(f"エラー: ファイル '{filename}' の読み込み権限がありません")
    except UnicodeDecodeError:
        print(f"エラー: ファイル '{filename}' の文字コードが正しくありません")
    except IOError as e:
        print(f"エラー: ファイル読み込み中に問題が発生しました - {e}")

# 発展版:複数ファイルの統計を取るバージョン
def multi_file_statistics():
    """複数ファイルの統計情報"""

    import os

    print("複数ファイルの統計情報")
    print("現在のディレクトリ内のテキストファイルを分析します")

    text_files = [f for f in os.listdir('.') 
                 if os.path.isfile(f) and f.endswith('.txt')]

    if not text_files:
        print("テキストファイルが見つかりません")
        return

    print(f"\n見つかったテキストファイル: {len(text_files)}件")

    statistics = []

    for filename in text_files:
        try:
            with open(filename, 'r', encoding='utf-8') as file:
                lines = file.readlines()
                line_count = len(lines)
                word_count = sum(len(line.split()) for line in lines)
                char_count = sum(len(line.rstrip('\n')) for line in lines)
                file_size = os.path.getsize(filename)

                stats = {
                    'filename': filename,
                    'lines': line_count,
                    'words': word_count,
                    'chars': char_count,
                    'size': file_size
                }
                statistics.append(stats)

        except Exception as e:
            print(f"ファイル '{filename}' の読み込み中にエラー: {e}")

    # 統計情報の表示
    if statistics:
        print("\nファイル統計:")
        print("-" * 70)
        print(f"{'ファイル名':<20} {'行数':<6} {'単語数':<6} {'文字数':<6} {'サイズ':<8}")
        print("-" * 70)

        for stats in statistics:
            print(f"{stats['filename']:<20} {stats['lines']:<6} {stats['words']:<6} "
                  f"{stats['chars']:<6} {stats['size']:<8} bytes")

        # 合計計算
        total_lines = sum(s['lines'] for s in statistics)
        total_words = sum(s['words'] for s in statistics)
        total_chars = sum(s['chars'] for s in statistics)
        total_size = sum(s['size'] for s in statistics)

        print("-" * 70)
        print(f"{'合計':<20} {total_lines:<6} {total_words:<6} {total_chars:<6} {total_size:<8} bytes")

# メイン実行
if __name__ == "__main__":
    print("=== 問題3 解答 ===")
    count_file_lines()

    # 複数ファイル統計版を実行する場合はコメントを外す
    # print("\n=== 複数ファイル統計版 ===")
    # multi_file_statistics()

実行結果:

=== 問題3 解答 ===
行数をカウントするファイル名を入力してください: python_learning.txt

ファイル 'python_learning.txt' の統計:
行数: 3
単語数: 9
文字数: 48

中級問題 解答

問題4 解答

import csv

def csv_student_management():
    """CSVファイルを使った学生データ管理"""

    # CSVファイルへの書き込み
    student_data = [
        ['名前', '科目', '点数'],
        ['山田太郎', '数学', 85],
        ['佐藤花子', '英語', 92],
        ['鈴木一郎', '科学', 78]
    ]

    # CSVファイルの作成
    try:
        with open('students.csv', 'w', encoding='utf-8', newline='') as file:
            writer = csv.writer(file)
            writer.writerows(student_data)
        print("学生データをCSVファイルに保存しました")

    except IOError as e:
        print(f"CSVファイルの書き込み中にエラー: {e}")
        return

    # CSVファイルの読み込みと表示
    try:
        print("\n学生データ一覧:")
        print("-" * 30)

        with open('students.csv', 'r', encoding='utf-8') as file:
            reader = csv.reader(file)
            header = next(reader)  # ヘッダー行を読み込み
            print(f"{header[0]:<10} {header[1]:<6} {header[2]}")
            print("-" * 30)

            for row in reader:
                print(f"{row[0]:<10} {row[1]:<6} {row[2]}")

    except FileNotFoundError:
        print("CSVファイルが見つかりません")
    except Exception as e:
        print(f"CSVファイルの読み込み中にエラー: {e}")

# 発展版:辞書形式でのCSV操作
def enhanced_csv_operations():
    """拡張版CSV操作(辞書形式)"""

    students = [
        {'名前': '山田太郎', '科目': '数学', '点数': 85},
        {'名前': '佐藤花子', '科目': '英語', '点数': 92},
        {'名前': '鈴木一郎', '科目': '科学', '点数': 78},
        {'名前': '田中美咲', '科目': '数学', '点数': 88},
        {'名前': '小林健太', '科目': '英語', '点数': 95}
    ]

    # 辞書形式でのCSV書き込み
    try:
        with open('students_dict.csv', 'w', encoding='utf-8', newline='') as file:
            fieldnames = ['名前', '科目', '点数']
            writer = csv.DictWriter(file, fieldnames=fieldnames)

            writer.writeheader()  # ヘッダー行を書き込み
            writer.writerows(students)

        print("辞書形式で学生データをCSVファイルに保存しました")

    except IOError as e:
        print(f"CSVファイルの書き込み中にエラー: {e}")
        return

    # 辞書形式でのCSV読み込みと分析
    try:
        with open('students_dict.csv', 'r', encoding='utf-8') as file:
            reader = csv.DictReader(file)

            print("\n学生データ分析:")
            print("-" * 40)

            # 科目ごとの平均点を計算
            subject_scores = {}
            for row in reader:
                subject = row['科目']
                score = int(row['点数'])

                if subject not in subject_scores:
                    subject_scores[subject] = []
                subject_scores[subject].append(score)

            # 科目ごとの統計を表示
            for subject, scores in subject_scores.items():
                average = sum(scores) / len(scores)
                highest = max(scores)
                lowest = min(scores)

                print(f"{subject}:")
                print(f"  平均点: {average:.1f}")
                print(f"  最高点: {highest}")
                print(f"  最低点: {lowest}")
                print(f"  受験者数: {len(scores)}")
                print()

    except Exception as e:
        print(f"CSVファイルの分析中にエラー: {e}")

# メイン実行
if __name__ == "__main__":
    print("=== 問題4 解答 ===")
    csv_student_management()

    print("\n=== 拡張版 ===")
    enhanced_csv_operations()

実行結果:

=== 問題4 解答 ===
学生データをCSVファイルに保存しました

学生データ一覧:
------------------------------
名前 科目 点数
------------------------------
山田太郎 数学 85
佐藤花子 英語 92
鈴木一郎 科学 78

=== 拡張版 ===
辞書形式で学生データをCSVファイルに保存しました

学生データ分析:
----------------------------------------
数学:
平均点: 86.5
最高点: 88
最低点: 85
受験者数: 2

英語:
平均点: 93.5
最高点: 95
最低点: 92
受験者数: 2

科学:
平均点: 78.0
最高点: 78
最低点: 78
受験者数: 1

問題5 解答

import json
import datetime

def json_config_management():
    """JSON設定ファイルの管理"""

    # 設定データの作成
    config_data = {
        "app_name": "MyPythonApp",
        "version": "1.0.0",
        "author": "山田太郎",
        "created_date": datetime.datetime.now().isoformat(),
        "settings": {
            "theme": "dark",
            "language": "ja",
            "auto_save": True,
            "max_file_size": 10485760
        },
        "features": {
            "export_pdf": True,
            "import_csv": True,
            "cloud_sync": False
        }
    }

    # JSONファイルへの書き込み
    try:
        with open('app_config.json', 'w', encoding='utf-8') as file:
            json.dump(config_data, file, ensure_ascii=False, indent=2)
        print("設定ファイルをJSON形式で保存しました")

    except IOError as e:
        print(f"設定ファイルの書き込み中にエラー: {e}")
        return

    # JSONファイルからの読み込み
    try:
        with open('app_config.json', 'r', encoding='utf-8') as file:
            loaded_config = json.load(file)

        print("\n読み込まれた設定:")
        print("=" * 50)
        print(f"アプリケーション名: {loaded_config['app_name']}")
        print(f"バージョン: {loaded_config['version']}")
        print(f"作者: {loaded_config['author']}")
        print(f"作成日: {loaded_config['created_date']}")

        print("\n設定項目:")
        for key, value in loaded_config['settings'].items():
            print(f"  {key}: {value}")

        print("\n機能:")
        for feature, enabled in loaded_config['features'].items():
            status = "有効" if enabled else "無効"
            print(f"  {feature}: {status}")

    except FileNotFoundError:
        print("設定ファイルが見つかりません")
    except json.JSONDecodeError:
        print("設定ファイルの形式が正しくありません")
    except Exception as e:
        print(f"設定ファイルの読み込み中にエラー: {e}")

# 発展版:設定の更新機能付き
def interactive_config_manager():
    """インタラクティブな設定管理"""

    config_file = 'config.json'

    def load_config():
        """設定を読み込む"""
        try:
            with open(config_file, 'r', encoding='utf-8') as file:
                return json.load(file)
        except FileNotFoundError:
            # デフォルト設定
            return {
                "app_name": "MyApp",
                "version": "1.0.0",
                "settings": {}
            }
        except Exception as e:
            print(f"設定の読み込み中にエラー: {e}")
            return None

    def save_config(config):
        """設定を保存する"""
        try:
            with open(config_file, 'w', encoding='utf-8') as file:
                json.dump(config, file, ensure_ascii=False, indent=2)
            print("設定を保存しました")
            return True
        except Exception as e:
            print(f"設定の保存中にエラー: {e}")
            return False

    def show_config(config):
        """設定を表示する"""
        print("\n現在の設定:")
        print("-" * 30)
        for key, value in config.items():
            if isinstance(value, dict):
                print(f"{key}:")
                for k, v in value.items():
                    print(f"  {k}: {v}")
            else:
                print(f"{key}: {value}")

    # メインループ
    config = load_config()
    if config is None:
        return

    print("設定管理システム")
    print("利用可能なコマンド: show, set  , save, quit")

    while True:
        command = input("\nコマンドを入力してください: ").strip().split()

        if not command:
            continue

        action = command[0].lower()

        if action == 'quit':
            # 変更があれば保存確認
            save_config(config)
            break

        elif action == 'show':
            show_config(config)

        elif action == 'save':
            save_config(config)

        elif action == 'set' and len(command) >= 3:
            key = command[1]
            value = ' '.join(command[2:])

            # 値の型変換を試みる
            if value.lower() == 'true':
                value = True
            elif value.lower() == 'false':
                value = False
            elif value.isdigit():
                value = int(value)
            elif value.replace('.', '').isdigit():
                value = float(value)

            config[key] = value
            print(f"設定を更新しました: {key} = {value}")

        else:
            print("無効なコマンドです")

# メイン実行
if __name__ == "__main__":
    print("=== 問題5 解答 ===")
    json_config_management()

    # インタラクティブ版を実行する場合はコメントを外す
    # print("\n=== インタラクティブ版 ===")
    # interactive_config_manager()

実行結果:

=== 問題5 解答 ===
設定ファイルをJSON形式で保存しました

読み込まれた設定:
==================================================
アプリケーション名: MyPythonApp
バージョン: 1.0.0
作者: 山田太郎
作成日: 2024-01-15T10:30:00.000000

設定項目:
theme: dark
language: ja
auto_save: True
max_file_size: 10485760

機能:
export_pdf: 有効
import_csv: 有効
cloud_sync: 無効

問題6 解答

import datetime
import logging

def simple_logging_system():
    """シンプルなログ記録システム"""

    def write_log(message, level="INFO"):
        """ログをファイルに記録"""
        timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        log_entry = f"[{timestamp}] [{level}] {message}\n"

        try:
            with open('application.log', 'a', encoding='utf-8') as log_file:
                log_file.write(log_entry)
            print(log_entry.strip())  # コンソールにも表示
            return True
        except IOError as e:
            print(f"ログの書き込み中にエラー: {e}")
            return False

    # ログの使用例
    print("ログ記録システムのデモ")
    print("-" * 40)

    write_log("アプリケーションを起動しました")
    write_log("データベースに接続しました", "INFO")
    write_log("ユーザー 'alice' がログインしました", "INFO")
    write_log("ファイルの読み込みに失敗しました", "WARNING")
    write_log("メモリ不足の可能性があります", "WARNING")
    write_log("データベース接続がタイムアウトしました", "ERROR")
    write_log("アプリケーションを終了します", "INFO")

    # ログファイルの内容を表示
    print("\nログファイルの内容:")
    print("-" * 40)
    try:
        with open('application.log', 'r', encoding='utf-8') as log_file:
            print(log_file.read())
    except FileNotFoundError:
        print("ログファイルが見つかりません")

# 発展版:標準loggingモジュールを使用
def advanced_logging_system():
    """標準loggingモジュールを使用した高度なログシステム"""

    # ロガーの設定
    logger = logging.getLogger('MyApp')
    logger.setLevel(logging.DEBUG)

    # ファイルハンドラーの設定
    file_handler = logging.FileHandler('advanced_app.log', encoding='utf-8')
    file_handler.setLevel(logging.DEBUG)

    # コンソールハンドラーの設定
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)

    # フォーマッターの設定
    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )

    file_handler.setFormatter(formatter)
    console_handler.setFormatter(formatter)

    # ハンドラーの追加
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)

    # ログの記録例
    print("高度なログシステムのデモ")
    print("-" * 40)

    logger.debug("デバッグ情報: 変数の値など")
    logger.info("アプリケーションの起動")
    logger.warning("警告: リソースが不足しています")
    logger.error("エラー: ファイルが見つかりません")
    logger.critical("重大なエラー: システムが停止します")

    # ログファイルの統計
    try:
        with open('advanced_app.log', 'r', encoding='utf-8') as log_file:
            lines = log_file.readlines()
            print(f"\nログファイルの統計:")
            print(f"  総ログ数: {len(lines)}")

            # レベルごとのカウント
            level_counts = {'DEBUG': 0, 'INFO': 0, 'WARNING': 0, 'ERROR': 0, 'CRITICAL': 0}
            for line in lines:
                for level in level_counts.keys():
                    if level in line:
                        level_counts[level] += 1
                        break

            for level, count in level_counts.items():
                if count > 0:
                    print(f"  {level}: {count}件")

    except FileNotFoundError:
        print("ログファイルが見つかりません")

# ログ分析機能
def log_analyzer():
    """ログファイルの分析"""

    log_file = 'application.log'

    try:
        with open(log_file, 'r', encoding='utf-8') as file:
            logs = file.readlines()

        print(f"ログ分析: {log_file}")
        print("-" * 50)
        print(f"総ログエントリ数: {len(logs)}")

        # 時間帯ごとのログ数
        hourly_counts = {}
        for log in logs:
            # タイムスタンプから時間を抽出
            if '[' in log and ']' in log:
                timestamp_str = log.split('[')[1].split(']')[0]
                try:
                    hour = timestamp_str.split(':')[0][-2:]  # 時間部分を取得
                    hourly_counts[hour] = hourly_counts.get(hour, 0) + 1
                except:
                    pass

        print("\n時間帯ごとのログ数:")
        for hour in sorted(hourly_counts.keys()):
            print(f"  {hour}時: {hourly_counts[hour]}件")

        # レベルごとの統計
        levels = ['INFO', 'WARNING', 'ERROR']
        level_counts = {level: 0 for level in levels}

        for log in logs:
            for level in levels:
                if f'[{level}]' in log:
                    level_counts[level] += 1
                    break

        print("\nレベルごとの統計:")
        for level, count in level_counts.items():
            if count > 0:
                percentage = (count / len(logs)) * 100
                print(f"  {level}: {count}件 ({percentage:.1f}%)")

        # エラーログの抽出
        error_logs = [log.strip() for log in logs if '[ERROR]' in log]
        if error_logs:
            print(f"\nエラーログ ({len(error_logs)}件):")
            for error_log in error_logs[:5]:  # 最初の5件のみ表示
                print(f"  - {error_log}")

    except FileNotFoundError:
        print(f"ログファイル '{log_file}' が見つかりません")

# メイン実行
if __name__ == "__main__":
    print("=== 問題6 解答 ===")
    simple_logging_system()

    print("\n=== 高度なログシステム ===")
    advanced_logging_system()

    print("\n=== ログ分析 ===")
    log_analyzer()

実行結果:

=== 問題6 解答 ===
ログ記録システムのデモ
----------------------------------------
[2024-01-15 10:30:01] [INFO] アプリケーションを起動しました
[2024-01-15 10:30:01] [INFO] データベースに接続しました
[2024-01-15 10:30:02] [INFO] ユーザー 'alice' がログインしました
[2024-01-15 10:30:03] [WARNING] ファイルの読み込みに失敗しました
[2024-01-15 10:30:04] [WARNING] メモリ不足の可能性があります
[2024-01-15 10:30:05] [ERROR] データベース接続がタイムアウトしました
[2024-01-15 10:30:06] [INFO] アプリケーションを終了します

ログファイルの内容:
----------------------------------------
[2024-01-15 10:30:01] [INFO] アプリケーションを起動しました
[2024-01-15 10:30:01] [INFO] データベースに接続しました
[2024-01-15 10:30:02] [INFO] ユーザー 'alice' がログインしました
[2024-01-15 10:30:03] [WARNING] ファイルの読み込みに失敗しました
[2024-01-15 10:30:04] [WARNING] メモリ不足の可能性があります
[2024-01-15 10:30:05] [ERROR] データベース接続がタイムアウトしました
[2024-01-15 10:30:06] [INFO] アプリケーションを終了します

問題7 解答

def compare_files():
    """2つのファイルを比較"""

    file1 = input("1つ目のファイル名: ")
    file2 = input("2つ目のファイル名: ")

    try:
        # ファイルの読み込み
        with open(file1, 'r', encoding='utf-8') as f1, \
             open(file2, 'r', encoding='utf-8') as f2:

            lines1 = f1.readlines()
            lines2 = f2.readlines()

        # 比較結果の格納
        same_lines = []
        different_lines = []
        file1_only = []
        file2_only = []

        max_lines = max(len(lines1), len(lines2))

        for i in range(max_lines):
            line1 = lines1[i].rstrip('\n') if i < len(lines1) else None
            line2 = lines2[i].rstrip('\n') if i < len(lines2) else None

            if line1 == line2:
                same_lines.append((i + 1, line1))
            elif line1 is None:
                file2_only.append((i + 1, line2))
            elif line2 is None:
                file1_only.append((i + 1, line1))
            else:
                different_lines.append((i + 1, line1, line2))

        # 結果のファイル出力
        with open('same_lines.txt', 'w', encoding='utf-8') as f:
            for line_num, line in same_lines:
                f.write(f"{line_num}: {line}\n")

        with open('different_lines.txt', 'w', encoding='utf-8') as f:
            for line_num, line1, line2 in different_lines:
                f.write(f"行{line_num}:\n")
                f.write(f"  ファイル1: {line1}\n")
                f.write(f"  ファイル2: {line2}\n")
                f.write("\n")

        with open('file1_only.txt', 'w', encoding='utf-8') as f:
            for line_num, line in file1_only:
                f.write(f"{line_num}: {line}\n")

        with open('file2_only.txt', 'w', encoding='utf-8') as f:
            for line_num, line in file2_only:
                f.write(f"{line_num}: {line}\n")

        # 統計情報の表示
        print(f"\n比較結果:")
        print(f"  同じ行: {len(same_lines)}行")
        print(f"  異なる行: {len(different_lines)}行")
        print(f"  {file1} のみ: {len(file1_only)}行")
        print(f"  {file2} のみ: {len(file2_only)}行")
        print(f"  総行数: {max_lines}行")

        # 一致率の計算
        if max_lines > 0:
            similarity = (len(same_lines) / max_lines) * 100
            print(f"  一致率: {similarity:.1f}%")

        print(f"\n結果ファイルが作成されました:")
        print(f"  same_lines.txt - 同じ行")
        print(f"  different_lines.txt - 異なる行")
        print(f"  file1_only.txt - {file1} のみの行")
        print(f"  file2_only.txt - {file2} のみの行")

    except FileNotFoundError as e:
        print(f"ファイルが見つかりません: {e}")
    except Exception as e:
        print(f"エラーが発生しました: {e}")

# 発展版:詳細な比較レポート
def detailed_file_comparison():
    """詳細なファイル比較レポート"""

    def read_file_lines(filename):
        """ファイルを行ごとに読み込み"""
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                return [line.rstrip('\n') for line in f]
        except FileNotFoundError:
            print(f"エラー: ファイル '{filename}' が見つかりません")
            return None
        except Exception as e:
            print(f"エラー: {e}")
            return None

    file1 = input("1つ目のファイル名: ")
    file2 = input("2つ目のファイル名: ")

    lines1 = read_file_lines(file1)
    lines2 = read_file_lines(file2)

    if lines1 is None or lines2 is None:
        return

    # セットを使った比較
    set1 = set(lines1)
    set2 = set(lines2)

    common_lines = set1 & set2
    unique_to_file1 = set1 - set2
    unique_to_file2 = set2 - set1

    # レポートの生成
    report = f"""ファイル比較レポート
生成日時: {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
比較ファイル: {file1} vs {file2}

基本統計:
  ファイル1の行数: {len(lines1)}
  ファイル2の行数: {len(lines2)}
  共通行数: {len(common_lines)}
  ファイル1のみの行数: {len(unique_to_file1)}
  ファイル2のみの行数: {len(unique_to_file2)}

共通行 ({len(common_lines)}行):
"""

    for i, line in enumerate(sorted(common_lines), 1):
        report += f"  {i}. {line}\n"

    report += f"\n{file1} のみの行 ({len(unique_to_file1)}行):\n"
    for i, line in enumerate(sorted(unique_to_file1), 1):
        report += f"  {i}. {line}\n"

    report += f"\n{file2} のみの行 ({len(unique_to_file2)}行):\n"
    for i, line in enumerate(sorted(unique_to_file2), 1):
        report += f"  {i}. {line}\n"

    # レポートの保存
    with open('file_comparison_report.txt', 'w', encoding='utf-8') as f:
        f.write(report)

    print("詳細な比較レポートを 'file_comparison_report.txt' に保存しました")

# メイン実行
if __name__ == "__main__":
    print("=== 問題7 解答 ===")
    compare_files()

    # 詳細比較版を実行する場合はコメントを外す
    # print("\n=== 詳細比較版 ===")
    # detailed_file_comparison()

実行結果:

=== 問題7 解答 ===
1つ目のファイル名: file1.txt
2つ目のファイル名: file2.txt

比較結果:
同じ行: 8行
異なる行: 3行
file1.txt のみ: 2行
file2.txt のみ: 1行
総行数: 14行
一致率: 57.1%

結果ファイルが作成されました:
same_lines.txt - 同じ行
different_lines.txt - 異なる行
file1_only.txt - file1.txt のみの行
file2_only.txt - file2.txt のみの行

問題8 解答

import os
import csv

def list_text_files():
    """テキストファイルの一覧をCSVに出力"""

    target_dir = input("検索するディレクトリ(空白で現在のディレクトリ): ").strip()
    if not target_dir:
        target_dir = '.'

    if not os.path.exists(target_dir):
        print(f"ディレクトリ '{target_dir}' が見つかりません")
        return

    if not os.path.isdir(target_dir):
        print(f"'{target_dir}' はディレクトリではありません")
        return

    # テキストファイルの検索
    text_files = []
    total_size = 0

    print(f"\n'{target_dir}' 内のテキストファイルを検索中...")

    for item in os.listdir(target_dir):
        item_path = os.path.join(target_dir, item)

        if os.path.isfile(item_path) and item.lower().endswith('.txt'):
            try:
                file_size = os.path.getsize(item_path)
                total_size += file_size

                # ファイルの行数と最終更新日時も取得
                line_count = 0
                with open(item_path, 'r', encoding='utf-8') as f:
                    for line in f:
                        line_count += 1

                modified_time = os.path.getmtime(item_path)
                modified_str = datetime.datetime.fromtimestamp(modified_time).strftime('%Y-%m-%d %H:%M:%S')

                text_files.append({
                    'filename': item,
                    'size': file_size,
                    'lines': line_count,
                    'modified': modified_str
                })

            except Exception as e:
                print(f"ファイル '{item}' の処理中にエラー: {e}")

    if not text_files:
        print("テキストファイルが見つかりませんでした")
        return

    # CSVファイルへの書き込み
    csv_filename = 'text_files_report.csv'

    try:
        with open(csv_filename, 'w', encoding='utf-8', newline='') as csvfile:
            fieldnames = ['filename', 'size', 'lines', 'modified']
            writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

            writer.writeheader()
            writer.writerows(text_files)

        print(f"\n{len(text_files)} 個のテキストファイル情報を '{csv_filename}' に保存しました")

    except IOError as e:
        print(f"CSVファイルの書き込み中にエラー: {e}")
        return

    # 統計情報の表示
    print(f"\n統計情報:")
    print(f"  テキストファイル数: {len(text_files)}")
    print(f"  総ファイルサイズ: {total_size} バイト ({total_size/1024:.2f} KB)")

    if text_files:
        avg_size = total_size / len(text_files)
        max_file = max(text_files, key=lambda x: x['size'])
        min_file = min(text_files, key=lambda x: x['size'])

        print(f"  平均ファイルサイズ: {avg_size:.2f} バイト")
        print(f"  最大ファイル: {max_file['filename']} ({max_file['size']} バイト)")
        print(f"  最小ファイル: {min_file['filename']} ({min_file['size']} バイト)")

    # CSVファイルの内容を表示
    print(f"\nCSVファイルの内容:")
    print("-" * 80)
    try:
        with open(csv_filename, 'r', encoding='utf-8') as csvfile:
            reader = csv.reader(csvfile)
            for row in reader:
                print(f"{row[0]:<20} {row[1]:>10} {row[2]:>8} {row[3]:>20}")
    except FileNotFoundError:
        print("CSVファイルが見つかりません")

# 発展版:再帰的な検索
def recursive_text_file_search():
    """再帰的にテキストファイルを検索"""

    base_dir = input("検索を開始するディレクトリ: ").strip() or '.'

    if not os.path.exists(base_dir):
        print(f"ディレクトリ '{base_dir}' が見つかりません")
        return

    text_files = []

    print(f"'{base_dir}' 以下のテキストファイルを再帰的に検索中...")

    for root, dirs, files in os.walk(base_dir):
        for file in files:
            if file.lower().endswith('.txt'):
                file_path = os.path.join(root, file)
                try:
                    file_size = os.path.getsize(file_path)
                    relative_path = os.path.relpath(file_path, base_dir)

                    # 行数のカウント
                    line_count = 0
                    with open(file_path, 'r', encoding='utf-8') as f:
                        for line in f:
                            line_count += 1

                    text_files.append({
                        'path': relative_path,
                        'size': file_size,
                        'lines': line_count,
                        'directory': root
                    })

                except Exception as e:
                    print(f"ファイル '{file_path}' の処理中にエラー: {e}")

    if not text_files:
        print("テキストファイルが見つかりませんでした")
        return

    # 結果の保存
    csv_filename = 'recursive_text_files.csv'

    with open(csv_filename, 'w', encoding='utf-8', newline='') as csvfile:
        fieldnames = ['path', 'size', 'lines', 'directory']
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

        writer.writeheader()
        writer.writerows(text_files)

    print(f"\n{len(text_files)} 個のテキストファイル情報を '{csv_filename}' に保存しました")

    # ディレクトリごとの集計
    dir_stats = {}
    for file_info in text_files:
        dir_path = file_info['directory']
        if dir_path not in dir_stats:
            dir_stats[dir_path] = {'count': 0, 'total_size': 0, 'total_lines': 0}

        dir_stats[dir_path]['count'] += 1
        dir_stats[dir_path]['total_size'] += file_info['size']
        dir_stats[dir_path]['total_lines'] += file_info['lines']

    # ディレクトリ統計の保存
    dir_csv = 'directory_stats.csv'
    with open(dir_csv, 'w', encoding='utf-8', newline='') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(['Directory', 'File Count', 'Total Size', 'Total Lines'])

        for dir_path, stats in sorted(dir_stats.items()):
            writer.writerow([dir_path, stats['count'], stats['total_size'], stats['total_lines']])

    print(f"ディレクトリごとの統計を '{dir_csv}' に保存しました")

# メイン実行
if __name__ == "__main__":
    print("=== 問題8 解答 ===")
    list_text_files()

    # 再帰検索版を実行する場合はコメントを外す
    # print("\n=== 再帰検索版 ===")
    # recursive_text_file_search()

実行結果:

=== 問題8 解答 ===
検索するディレクトリ(空白で現在のディレクトリ):

'.' 内のテキストファイルを検索中...

5 個のテキストファイル情報を 'text_files_report.csv' に保存しました

統計情報:
テキストファイル数: 5
総ファイルサイズ: 2450 バイト (2.39 KB)
平均ファイルサイズ: 490.00 バイト
最大ファイル: large_demo.txt (1200 バイト)
最小ファイル: file1_only.txt (85 バイト)

CSVファイルの内容:
--------------------------------------------------------------------------------
filename size lines modified
application.log 450 7 2024-01-15 10:30:06
file1_only.txt 85 2 2024-01-15 10:25:12
file2_only.txt 92 1 2024-01-15 10:25:12
large_demo.txt 1200 10000 2024-01-15 10:35:20
python_learning.txt 623 3 2024-01-15 10:20:15

問題9 解答

def search_keyword_in_file():
    """ファイル内のキーワード検索"""

    input_file = input("検索対象のファイル名: ")
    keyword = input("検索するキーワード: ")
    output_file = input("結果を保存するファイル名: ")

    if not all([input_file, keyword, output_file]):
        print("すべての入力項目を指定してください")
        return

    try:
        matches_found = 0

        with open(input_file, 'r', encoding='utf-8') as infile, \
             open(output_file, 'w', encoding='utf-8') as outfile:

            outfile.write(f"キーワード '{keyword}' の検索結果\n")
            outfile.write(f"検索対象ファイル: {input_file}\n")
            outfile.write(f"検索日時: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            outfile.write("=" * 50 + "\n\n")

            line_number = 0
            for line in infile:
                line_number += 1
                if keyword in line:
                    matches_found += 1
                    # 前後の文脈も含めて保存
                    outfile.write(f"行 {line_number}:\n")
                    outfile.write(f"  {line.strip()}\n\n")

            outfile.write(f"\n合計 {matches_found} 件見つかりました\n")

        print(f"検索完了: {matches_found} 件見つかりました")
        print(f"結果は '{output_file}' に保存されました")

        # 結果のプレビュー
        if matches_found > 0:
            preview = input("\n結果をプレビューしますか? (y/n): ")
            if preview.lower() == 'y':
                print(f"\n検索結果のプレビュー (最初の5件):")
                print("-" * 50)
                with open(output_file, 'r', encoding='utf-8') as f:
                    lines = f.readlines()[:15]  # 最初の15行を表示
                    for line in lines:
                        print(line, end='')

    except FileNotFoundError:
        print(f"ファイル '{input_file}' が見つかりません")
    except Exception as e:
        print(f"エラーが発生しました: {e}")

# 発展版:高度な検索機能
def advanced_file_search():
    """高度なファイル検索機能"""

    def search_files(directory, keyword, file_extensions=None, case_sensitive=False):
        """ディレクトリ内のファイルを検索"""
        if file_extensions is None:
            file_extensions = ['.txt']

        matches = []

        for root, dirs, files in os.walk(directory):
            for file in files:
                # ファイル拡張子のチェック
                if any(file.lower().endswith(ext.lower()) for ext in file_extensions):
                    file_path = os.path.join(root, file)
                    try:
                        with open(file_path, 'r', encoding='utf-8') as f:
                            line_number = 0
                            for line in f:
                                line_number += 1
                                search_line = line if case_sensitive else line.lower()
                                search_keyword = keyword if case_sensitive else keyword.lower()

                                if search_keyword in search_line:
                                    matches.append({
                                        'file': file_path,
                                        'line_number': line_number,
                                        'content': line.strip(),
                                        'context': get_context(line, f) if hasattr(f, 'tell') else ""
                                    })
                    except Exception as e:
                        print(f"ファイル '{file_path}' の読み込み中にエラー: {e}")

        return matches

    def get_context(matched_line, file_obj):
        """マッチした行の前後の文脈を取得"""
        try:
            current_pos = file_obj.tell()
            file_obj.seek(current_pos - 200)  # 200バイト前から読み込み

            context = file_obj.read(400)  # 400バイト分の文脈
            return context
        except:
            return ""

    def save_search_results(matches, output_file, keyword):
        """検索結果を保存"""
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(f"高度なファイル検索結果\n")
            f.write(f"検索キーワード: '{keyword}'\n")
            f.write(f"検索日時: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"見つかった件数: {len(matches)}\n")
            f.write("=" * 60 + "\n\n")

            current_file = None
            for match in matches:
                if match['file'] != current_file:
                    current_file = match['file']
                    f.write(f"\nファイル: {current_file}\n")
                    f.write("-" * 40 + "\n")

                f.write(f"行 {match['line_number']}:\n")
                f.write(f"  {match['content']}\n")
                if match['context']:
                    f.write(f"  文脈: ...{match['context']}...\n")
                f.write("\n")

    # メイン処理
    directory = input("検索ディレクトリ: ").strip() or '.'
    keyword = input("検索キーワード: ")
    output_file = input("結果ファイル名: ")

    extensions_input = input("ファイル拡張子 (カンマ区切り, 空白で全てのテキストファイル): ")
    if extensions_input.strip():
        extensions = [ext.strip() for ext in extensions_input.split(',')]
    else:
        extensions = ['.txt']

    case_sensitive = input("大文字小文字を区別しますか? (y/n): ").lower() == 'y'

    print("検索中...")
    matches = search_files(directory, keyword, extensions, case_sensitive)

    if matches:
        save_search_results(matches, output_file, keyword)
        print(f"検索完了: {len(matches)} 件見つかりました")
        print(f"結果は '{output_file}' に保存されました")
    else:
        print("該当する結果は見つかりませんでした")

# メイン実行
if __name__ == "__main__":
    print("=== 問題9 解答 ===")
    search_keyword_in_file()

    # 高度な検索版を実行する場合はコメントを外す
    # print("\n=== 高度な検索版 ===")
    # advanced_file_search()

実行結果:

=== 問題9 解答 ===
検索対象のファイル名: large_demo.txt
検索するキーワード: サンプル
結果を保存するファイル名: search_results.txt

検索完了: 10000 件見つかりました
結果は 'search_results.txt' に保存されました

結果をプレビューしますか? (y/n): y

検索結果のプレビュー (最初の5件):
--------------------------------------------------
キーワード 'サンプル' の検索結果
検索対象ファイル: large_demo.txt
検索日時: 2024-01-15 10:40:00
==================================================

行 1:
これは行 1 です。サンプルテキストが含まれています。

行 2:
これは行 2 です。サンプルテキストが含まれています。

行 3:
これは行 3 です。サンプルテキストが含まれています。

行 4:
これは行 4 です。サンプルテキストが含まれています。

行 5:
これは行 5 です。サンプルテキストが含まれています。

上級問題 解答

問題10 解答

import csv
import os

class SimpleUserDatabase:
    """シンプルなユーザーデータベース"""

    def __init__(self, csv_file='users.csv'):
        self.csv_file = csv_file
        self.fieldnames = ['名前', '年齢', 'メール']
        self._ensure_file_exists()

    def _ensure_file_exists(self):
        """CSVファイルが存在することを確認"""
        if not os.path.exists(self.csv_file):
            with open(self.csv_file, 'w', encoding='utf-8', newline='') as f:
                writer = csv.DictWriter(f, fieldnames=self.fieldnames)
                writer.writeheader()

    def _read_all_users(self):
        """すべてのユーザーを読み込む"""
        users = []
        try:
            with open(self.csv_file, 'r', encoding='utf-8') as f:
                reader = csv.DictReader(f)
                for row in reader:
                    users.append(row)
        except FileNotFoundError:
            pass
        return users

    def _write_all_users(self, users):
        """すべてのユーザーを書き込む"""
        with open(self.csv_file, 'w', encoding='utf-8', newline='') as f:
            writer = csv.DictWriter(f, fieldnames=self.fieldnames)
            writer.writeheader()
            writer.writerows(users)

    def add_user(self, name, age, email):
        """ユーザーを追加"""
        users = self._read_all_users()

        # メールアドレスの重複チェック
        for user in users:
            if user['メール'] == email:
                print(f"エラー: メールアドレス '{email}' は既に登録されています")
                return False

        new_user = {
            '名前': name,
            '年齢': age,
            'メール': email
        }

        users.append(new_user)
        self._write_all_users(users)
        print(f"ユーザー '{name}' を追加しました")
        return True

    def list_users(self):
        """全ユーザーを表示"""
        users = self._read_all_users()

        if not users:
            print("登録されているユーザーはいません")
            return

        print(f"\n登録ユーザー一覧 ({len(users)}名):")
        print("-" * 50)
        print(f"{'名前':<15} {'年齢':<6} {'メール':<25}")
        print("-" * 50)

        for user in users:
            print(f"{user['名前']:<15} {user['年齢']:<6} {user['メール']:<25}")

    def search_user(self, search_term):
        """ユーザーを検索"""
        users = self._read_all_users()
        results = []

        for user in users:
            if (search_term.lower() in user['名前'].lower() or 
                search_term in user['メール'] or 
                search_term == user['年齢']):
                results.append(user)

        if results:
            print(f"\n検索結果: '{search_term}' に一致するユーザー {len(results)}名")
            print("-" * 50)
            for user in results:
                print(f"名前: {user['名前']}, 年齢: {user['年齢']}, メール: {user['メール']}")
        else:
            print(f"'{search_term}' に一致するユーザーは見つかりませんでした")

        return results

    def update_user(self, email, new_name=None, new_age=None, new_email=None):
        """ユーザー情報を更新"""
        users = self._read_all_users()
        updated = False

        for user in users:
            if user['メール'] == email:
                if new_name:
                    user['名前'] = new_name
                if new_age:
                    user['年齢'] = new_age
                if new_email:
                    # 新しいメールアドレスの重複チェック
                    if new_email != email and any(u['メール'] == new_email for u in users):
                        print(f"エラー: メールアドレス '{new_email}' は既に使用されています")
                        return False
                    user['メール'] = new_email

                updated = True
                break

        if updated:
            self._write_all_users(users)
            print(f"ユーザー情報を更新しました")
            return True
        else:
            print(f"メールアドレス '{email}' のユーザーが見つかりません")
            return False

    def delete_user(self, email):
        """ユーザーを削除"""
        users = self._read_all_users()
        original_count = len(users)

        users = [user for user in users if user['メール'] != email]

        if len(users) < original_count:
            self._write_all_users(users)
            print(f"メールアドレス '{email}' のユーザーを削除しました")
            return True
        else:
            print(f"メールアドレス '{email}' のユーザーが見つかりません")
            return False

    def get_statistics(self):
        """統計情報を表示"""
        users = self._read_all_users()

        if not users:
            print("統計情報: ユーザーが登録されていません")
            return

        ages = [int(user['年齢']) for user in users]
        total_users = len(users)
        avg_age = sum(ages) / total_users
        min_age = min(ages)
        max_age = max(ages)

        print(f"\nユーザー統計:")
        print(f"  総ユーザー数: {total_users}名")
        print(f"  平均年齢: {avg_age:.1f}歳")
        print(f"  最小年齢: {min_age}歳")
        print(f"  最大年齢: {max_age}歳")

        # 年齢層別の分布
        age_groups = {'10代': 0, '20代': 0, '30代': 0, '40代以上': 0}
        for age in ages:
            if age < 20:
                age_groups['10代'] += 1
            elif age < 30:
                age_groups['20代'] += 1
            elif age < 40:
                age_groups['30代'] += 1
            else:
                age_groups['40代以上'] += 1

        print(f"\n年齢層別分布:")
        for group, count in age_groups.items():
            if count > 0:
                percentage = (count / total_users) * 100
                print(f"  {group}: {count}名 ({percentage:.1f}%)")

def user_database_interface():
    """ユーザーデータベースのインターフェース"""

    db = SimpleUserDatabase()

    print("シンプルユーザーデータベース")
    print("=" * 50)

    while True:
        print("\n利用可能な操作:")
        print("  1. ユーザー一覧表示")
        print("  2. ユーザー追加")
        print("  3. ユーザー検索")
        print("  4. ユーザー更新")
        print("  5. ユーザー削除")
        print("  6. 統計情報")
        print("  7. 終了")

        choice = input("\n操作を選択してください (1-7): ").strip()

        if choice == '1':
            db.list_users()

        elif choice == '2':
            print("\n新しいユーザーを追加:")
            name = input("名前: ")
            age = input("年齢: ")
            email = input("メールアドレス: ")

            if name and age and email:
                db.add_user(name, age, email)
            else:
                print("すべての項目を入力してください")

        elif choice == '3':
            search_term = input("検索する名前、年齢、またはメールアドレス: ")
            if search_term:
                db.search_user(search_term)
            else:
                print("検索語を入力してください")

        elif choice == '4':
            email = input("更新するユーザーのメールアドレス: ")
            if email:
                print("新しい情報を入力(変更しない項目は空白のままEnter):")
                new_name = input("新しい名前: ") or None
                new_age = input("新しい年齢: ") or None
                new_email = input("新しいメールアドレス: ") or None

                db.update_user(email, new_name, new_age, new_email)
            else:
                print("メールアドレスを入力してください")

        elif choice == '5':
            email = input("削除するユーザーのメールアドレス: ")
            if email:
                db.delete_user(email)
            else:
                print("メールアドレスを入力してください")

        elif choice == '6':
            db.get_statistics()

        elif choice == '7':
            print("ユーザーデータベースを終了します")
            break

        else:
            print("無効な選択です。1-7の数字を入力してください")

# メイン実行
if __name__ == "__main__":
    print("=== 問題10 解答 ===")
    user_database_interface()

実行結果:

=== 問題10 解答 ===
シンプルユーザーデータベース
==================================================

利用可能な操作:
1. ユーザー一覧表示
2. ユーザー追加
3. ユーザー検索
4. ユーザー更新
5. ユーザー削除
6. 統計情報
7. 終了

操作を選択してください (1-7): 2

新しいユーザーを追加:
名前: 山田太郎
年齢: 25
メールアドレス: yamada@example.com
ユーザー '山田太郎' を追加しました

操作を選択してください (1-7): 2

新しいユーザーを追加:
名前: 佐藤花子
年齢: 30
メールアドレス: sato@example.com
ユーザー '佐藤花子' を追加しました

操作を選択してください (1-7): 1

登録ユーザー一覧 (2名):
--------------------------------------------------
名前 年齢 メール
--------------------------------------------------
山田太郎 25 yamada@example.com
佐藤花子 30 sato@example.com

操作を選択してください (1-7): 6

ユーザー統計:
総ユーザー数: 2名
平均年齢: 27.5歳
最小年齢: 25歳
最大年齢: 30歳

年齢層別分布:
20代: 1名 (50.0%)
30代: 1名 (50.0%)

以下に問題11と問題12の解答例を示します。

問題11: ファイルバックアップシステム

import os
import shutil
import hashlib
from datetime import datetime
import json

class BackupSystem:
    def __init__(self, backup_dir="backup"):
        self.backup_dir = backup_dir
        self.manifest_file = os.path.join(backup_dir, "backup_manifest.json")

        # バックアップディレクトリがなければ作成
        if not os.path.exists(backup_dir):
            os.makedirs(backup_dir)

    def calculate_file_hash(self, filepath):
        """ファイルのMD5ハッシュを計算"""
        hash_md5 = hashlib.md5()
        try:
            with open(filepath, "rb") as f:
                for chunk in iter(lambda: f.read(4096), b""):
                    hash_md5.update(chunk)
            return hash_md5.hexdigest()
        except Exception as e:
            print(f"エラー: {filepath} のハッシュ計算に失敗: {e}")
            return None

    def load_manifest(self):
        """既存のバックアップマニフェストを読み込み"""
        if os.path.exists(self.manifest_file):
            try:
                with open(self.manifest_file, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except Exception as e:
                print(f"マニフェスト読み込みエラー: {e}")
        return {}

    def save_manifest(self, manifest):
        """バックアップマニフェストを保存"""
        try:
            with open(self.manifest_file, 'w', encoding='utf-8') as f:
                json.dump(manifest, f, indent=2, ensure_ascii=False)
        except Exception as e:
            print(f"マニフェスト保存エラー: {e}")

    def get_files_to_backup(self, source_dir, last_backup_manifest):
        """バックアップが必要なファイルのリストを取得(増分バックアップ)"""
        files_to_backup = []

        for root, dirs, files in os.walk(source_dir):
            for file in files:
                filepath = os.path.join(root, file)
                relative_path = os.path.relpath(filepath, source_dir)

                # ファイルのハッシュを計算
                current_hash = self.calculate_file_hash(filepath)
                if current_hash is None:
                    continue

                # 前回のバックアップと比較
                if relative_path not in last_backup_manifest:
                    files_to_backup.append((filepath, relative_path, current_hash))
                elif last_backup_manifest[relative_path]['hash'] != current_hash:
                    files_to_backup.append((filepath, relative_path, current_hash))

        return files_to_backup

    def create_backup(self, source_dir):
        """バックアップを実行"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        backup_folder = os.path.join(self.backup_dir, f"backup_{timestamp}")

        # 既存のマニフェストを読み込み
        last_manifest = self.load_manifest()

        # バックアップが必要なファイルを取得
        files_to_backup = self.get_files_to_backup(source_dir, last_manifest)

        if not files_to_backup:
            print("バックアップが必要なファイルはありません")
            return

        # バックアップフォルダ作成
        os.makedirs(backup_folder)

        new_manifest = last_manifest.copy()
        backed_up_files = []

        # ファイルをバックアップ
        for filepath, relative_path, file_hash in files_to_backup:
            try:
                # サブディレクトリ構造を維持
                backup_filepath = os.path.join(backup_folder, relative_path)
                os.makedirs(os.path.dirname(backup_filepath), exist_ok=True)

                # ファイルコピー
                shutil.copy2(filepath, backup_filepath)

                # マニフェスト更新
                new_manifest[relative_path] = {
                    'hash': file_hash,
                    'size': os.path.getsize(filepath),
                    'last_modified': os.path.getmtime(filepath),
                    'backup_time': timestamp
                }

                backed_up_files.append(relative_path)
                print(f"バックアップ完了: {relative_path}")

            except Exception as e:
                print(f"バックアップ失敗: {relative_path} - {e}")

        # 新しいマニフェストを保存
        self.save_manifest(new_manifest)

        print(f"\nバックアップ完了: {len(backed_up_files)} ファイルをバックアップ")
        print(f"バックアップ先: {backup_folder}")

    def list_backups(self):
        """バックアップ一覧を表示"""
        if not os.path.exists(self.backup_dir):
            print("バックアップディレクトリが存在しません")
            return

        backups = []
        for item in os.listdir(self.backup_dir):
            if item.startswith("backup_") and os.path.isdir(os.path.join(self.backup_dir, item)):
                backups.append(item)

        if backups:
            print("利用可能なバックアップ:")
            for backup in sorted(backups):
                print(f"  - {backup}")
        else:
            print("バックアップがありません")

# 使用例
if __name__ == "__main__":
    backup_system = BackupSystem()

    # バックアップ実行例
    source_directory = "test_files"  # バックアップしたいディレクトリ
    backup_system.create_backup(source_directory)

    # バックアップ一覧表示
    backup_system.list_backups()

問題12: マークダウンファイル解析プログラム

import re
import json
from typing import List, Dict, Any
import sys

class MarkdownParser:
    def __init__(self):
        self.headings = []
        self.code_blocks = []
        self.links = []
        self.images = []

    def parse_file(self, filepath: str) -> Dict[str, Any]:
        """マークダウンファイルを解析"""
        try:
            with open(filepath, 'r', encoding='utf-8') as f:
                content = f.read()
        except Exception as e:
            print(f"ファイル読み込みエラー: {e}")
            return {}

        # 各要素を解析
        self._parse_headings(content)
        self._parse_code_blocks(content)
        self._parse_links_and_images(content)

        return self._get_structured_data()

    def _parse_headings(self, content: str):
        """見出しを解析"""
        # 見出しの正規表現 (#, ##, ### など)
        heading_pattern = r'^(#{1,6})\s+(.+)$'

        lines = content.split('\n')
        for line_num, line in enumerate(lines, 1):
            match = re.match(heading_pattern, line.strip())
            if match:
                level = len(match.group(1))  # #の数でレベルを決定
                text = match.group(2).strip()

                self.headings.append({
                    'level': level,
                    'text': text,
                    'line_number': line_num
                })

    def _parse_code_blocks(self, content: str):
        """コードブロックを抽出"""
        # コードブロックの正規表現 (```language と ```)
        code_block_pattern = r'```(\w*)\n(.*?)\n```'

        matches = re.finditer(code_block_pattern, content, re.DOTALL)

        for match in matches:
            language = match.group(1) or 'plaintext'
            code_content = match.group(2)

            self.code_blocks.append({
                'language': language,
                'content': code_content,
                'line_count': len(code_content.split('\n'))
            })

    def _parse_links_and_images(self, content: str):
        """リンクと画像を抽出"""
        # リンクの正規表現 [text](url)
        link_pattern = r'\[([^\]]+)\]\(([^)]+)\)'

        # 画像の正規表現 ![alt](url)
        image_pattern = r'!\[([^\]]*)\]\(([^)]+)\)'

        lines = content.split('\n')

        for line_num, line in enumerate(lines, 1):
            # リンクを検索
            link_matches = re.finditer(link_pattern, line)
            for match in link_matches:
                self.links.append({
                    'text': match.group(1),
                    'url': match.group(2),
                    'line_number': line_num
                })

            # 画像を検索
            image_matches = re.finditer(image_pattern, line)
            for match in image_matches:
                self.images.append({
                    'alt_text': match.group(1),
                    'url': match.group(2),
                    'line_number': line_num
                })

    def _get_structured_data(self) -> Dict[str, Any]:
        """解析結果を構造化して返す"""
        # 見出しの階層構造を構築
        structured_headings = self._build_heading_hierarchy()

        return {
            'document_stats': {
                'headings_count': len(self.headings),
                'code_blocks_count': len(self.code_blocks),
                'links_count': len(self.links),
                'images_count': len(self.images)
            },
            'headings': structured_headings,
            'code_blocks': self.code_blocks,
            'links': self.links,
            'images': self.images
        }

    def _build_heading_hierarchy(self) -> List[Dict[str, Any]]:
        """見出しの階層構造を構築"""
        if not self.headings:
            return []

        hierarchy = []
        stack = [{'children': hierarchy, 'level': 0}]

        for heading in self.headings:
            current_level = heading['level']

            # 現在のレベルより浅いスタック要素をポップ
            while stack[-1]['level'] >= current_level:
                stack.pop()

            # 新しい見出し要素を作成
            new_heading = {
                'level': current_level,
                'text': heading['text'],
                'line_number': heading['line_number'],
                'children': []
            }

            # 適切な親に追加
            stack[-1]['children'].append(new_heading)

            # スタックに追加(この要素が次の要素の親になる可能性がある)
            stack.append(new_heading)

        return hierarchy

    def export_to_json(self, data: Dict[str, Any], output_file: str = None):
        """解析結果をJSONで出力"""
        if output_file:
            with open(output_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=2, ensure_ascii=False)
            print(f"解析結果を {output_file} に出力しました")
        else:
            print(json.dumps(data, indent=2, ensure_ascii=False))

def main():
    """メイン関数"""
    if len(sys.argv) < 2:
        print("使用方法: python markdown_parser.py <マークダウンファイル> [出力JSONファイル]")
        sys.exit(1)

    markdown_file = sys.argv[1]
    output_file = sys.argv[2] if len(sys.argv) > 2 else None

    parser = MarkdownParser()
    result = parser.parse_file(markdown_file)

    if result:
        parser.export_to_json(result, output_file)
    else:
        print("解析に失敗しました")

# 使用例
if __name__ == "__main__":
    # コマンドライン引数がある場合はそれを使用
    if len(sys.argv) > 1:
        main()
    else:
        # テスト用のサンプル
        parser = MarkdownParser()

        # サンプルのマークダウンコンテンツ
        sample_markdown = """
# サンプルドキュメント

## はじめに

これは**サンプル**のマークダウンファイルです。

### 機能説明

以下の機能があります:

- 見出し解析
- コードブロック抽出
```python
def hello_world():
    print("Hello, World!")
    return True