Pythonファイル操作完全攻略【読み書き・パス操作を徹底マスター】

ファイル操作は、データの永続化、設定ファイルの管理、ログ記録など、プログラム開発において不可欠な技術です。Pythonでは豊富な標準ライブラリにより、テキストファイル、バイナリファイル、CSVファイルなど様々な形式のファイルを効率的に操作できます。本記事では、ファイル操作の基本から応用まで、実用的なサンプルコードとともに徹底解説します。

ファイル操作の基本概念

ファイル操作の主な用途

  • データ保存: アプリケーションデータの永続化
  • 設定管理: 設定ファイルの読み書き
  • ログ記録: エラーログ、アクセスログの出力
  • データ分析: CSV、JSONファイルの処理
  • バックアップ: ファイルのコピー、移動

ファイルモードの種類

モード 説明 用途
‘r’ 読み取り専用 ファイル読み込み
‘w’ 書き込み専用(上書き) ファイル作成・上書き
‘a’ 追記モード ログファイルへの追記
‘x’ 排他的作成 新規ファイル作成
‘b’ バイナリモード 画像、音声ファイル
‘t’ テキストモード テキストファイル(デフォルト)

基本的なファイル読み込み

open()関数の基本

# 基本的なファイル読み込み
with open('sample.txt', 'r', encoding='utf-8') as file:
    content = file.read()
    print(content)

行ごとの読み込み

# 1行ずつ読み込み
with open('data.txt', 'r', encoding='utf-8') as file:
    for line_num, line in enumerate(file, 1):
        print(f"{line_num}: {line.strip()}")

readlines()とreadline()

# 全行をリストで取得
with open('sample.txt', 'r', encoding='utf-8') as file:
    lines = file.readlines()
    print(f"行数: {len(lines)}")

# 1行ずつ読み込み
with open('sample.txt', 'r', encoding='utf-8') as file:
    first_line = file.readline().strip()
    second_line = file.readline().strip()
    print(f"1行目: {first_line}")
    print(f"2行目: {second_line}")

ファイル書き込み

基本的な書き込み

# ファイルに書き込み(上書き)
with open('output.txt', 'w', encoding='utf-8') as file:
    file.write("Hello, World!\n")
    file.write("Pythonファイル操作\n")

# 複数行の書き込み
lines = ["1行目\n", "2行目\n", "3行目\n"]
with open('multi_lines.txt', 'w', encoding='utf-8') as file:
    file.writelines(lines)

追記モード

# ファイルに追記
with open('log.txt', 'a', encoding='utf-8') as file:
    file.write("新しいログエントリ\n")

# タイムスタンプ付きログ
from datetime import datetime
with open('app.log', 'a', encoding='utf-8') as file:
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    file.write(f"[{timestamp}] アプリケーション開始\n")

print()関数でのファイル出力

# print()関数を使ったファイル出力
with open('print_output.txt', 'w', encoding='utf-8') as file:
    print("Hello", "World", file=file)
    print(f"計算結果: {2 + 3}", file=file)
    print("=" * 20, file=file)

pathlib モジュールの活用

パスの基本操作

from pathlib import Path

# パスオブジェクトの作成
file_path = Path('data/sample.txt')
print(file_path.name)      # sample.txt
print(file_path.suffix)    # .txt
print(file_path.parent)    # data
print(file_path.stem)      # sample

ディレクトリとファイルの存在確認

from pathlib import Path

path = Path('example.txt')
print(f"ファイル存在: {path.exists()}")
print(f"ファイル: {path.is_file()}")
print(f"ディレクトリ: {path.is_dir()}")

# ディレクトリ作成
output_dir = Path('output')
output_dir.mkdir(exist_ok=True)  # 既存でもエラーにしない

glob パターンでのファイル検索

from pathlib import Path

# カレントディレクトリの.txtファイルを取得
txt_files = list(Path('.').glob('*.txt'))
print(f"テキストファイル数: {len(txt_files)}")

# 再帰的検索
all_py_files = list(Path('.').rglob('*.py'))
for py_file in all_py_files[:5]:  # 最初の5個を表示
    print(py_file)

os と os.path モジュール

ファイル・ディレクトリ操作

import os

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

# ディレクトリ内容の取得
files = os.listdir('.')
print(f"ファイル数: {len(files)}")

# ディレクトリ作成
os.makedirs('new_folder/subfolder', exist_ok=True)

パス操作

import os.path

# パスの結合
file_path = os.path.join('data', 'files', 'sample.txt')
print(file_path)

# パス情報の取得
print(f"ディレクトリ: {os.path.dirname(file_path)}")
print(f"ファイル名: {os.path.basename(file_path)}")
print(f"拡張子: {os.path.splitext(file_path)[1]}")

# ファイル存在確認
if os.path.exists('config.txt'):
    size = os.path.getsize('config.txt')
    print(f"ファイルサイズ: {size} bytes")

CSVファイルの操作

CSV読み込み

import csv

# 基本的なCSV読み込み
with open('data.csv', 'r', encoding='utf-8') as file:
    reader = csv.reader(file)
    for row in reader:
        print(row)

# 辞書形式でのCSV読み込み
with open('employees.csv', 'r', encoding='utf-8') as file:
    reader = csv.DictReader(file)
    for row in reader:
        print(f"{row['name']}: {row['age']}歳")

CSV書き込み

import csv

# 基本的なCSV書き込み
data = [['名前', '年齢', '職業'], ['田中', '30', 'エンジニア'], ['佐藤', '25', 'デザイナー']]
with open('output.csv', 'w', newline='', encoding='utf-8') as file:
    writer = csv.writer(file)
    writer.writerows(data)

# 辞書データのCSV書き込み
employees = [
    {'name': '田中', 'age': 30, 'job': 'エンジニア'},
    {'name': '佐藤', 'age': 25, 'job': 'デザイナー'}
]
with open('employees_dict.csv', 'w', newline='', encoding='utf-8') as file:
    writer = csv.DictWriter(file, fieldnames=['name', 'age', 'job'])
    writer.writeheader()
    writer.writerows(employees)

バイナリファイルの操作

画像ファイルのコピー

# バイナリファイルのコピー
def copy_binary_file(src, dst):
    with open(src, 'rb') as src_file:
        with open(dst, 'wb') as dst_file:
            dst_file.write(src_file.read())

# 使用例
# copy_binary_file('image.jpg', 'image_copy.jpg')

ファイルサイズ制限付き読み込み

def read_large_file(filename, chunk_size=1024):
    """大きなファイルをチャンクごとに読み込み"""
    with open(filename, 'rb') as file:
        while True:
            chunk = file.read(chunk_size)
            if not chunk:
                break
            yield chunk

# 使用例
# for chunk in read_large_file('large_file.bin'):
#     process_chunk(chunk)  # チャンクごとに処理

一時ファイルの操作

tempfile モジュール

import tempfile
import os

# 一時ファイルの作成
with tempfile.NamedTemporaryFile(mode='w', delete=False, encoding='utf-8') as temp_file:
    temp_file.write("一時的なデータ")
    temp_name = temp_file.name

print(f"一時ファイル: {temp_name}")

# 一時ファイルの削除
os.unlink(temp_name)

# 一時ディレクトリ
with tempfile.TemporaryDirectory() as temp_dir:
    temp_file_path = os.path.join(temp_dir, 'temp.txt')
    with open(temp_file_path, 'w') as f:
        f.write("一時ファイル内容")
    print(f"一時ディレクトリ: {temp_dir}")
# with文を出ると自動的に削除される

ファイル情報の取得

ファイル属性の取得

import os
import stat
from datetime import datetime

def get_file_info(filename):
    if not os.path.exists(filename):
        return None
    
    file_stat = os.stat(filename)
    return {
        'size': file_stat.st_size,
        'modified': datetime.fromtimestamp(file_stat.st_mtime),
        'created': datetime.fromtimestamp(file_stat.st_ctime),
        'permissions': stat.filemode(file_stat.st_mode)
    }

# 使用例
info = get_file_info('sample.txt')
if info:
    print(f"サイズ: {info['size']} bytes")
    print(f"更新日時: {info['modified']}")
    print(f"権限: {info['permissions']}")

ディレクトリサイズの計算

import os

def get_directory_size(directory):
    total_size = 0
    for dirpath, dirnames, filenames in os.walk(directory):
        for filename in filenames:
            file_path = os.path.join(dirpath, filename)
            try:
                total_size += os.path.getsize(file_path)
            except OSError:
                pass  # アクセスできないファイルをスキップ
    return total_size

# 使用例
# size = get_directory_size('.')
# print(f"ディレクトリサイズ: {size / 1024 / 1024:.2f} MB")

エラーハンドリング

ファイル操作の例外処理

def safe_file_read(filename):
    try:
        with open(filename, 'r', encoding='utf-8') as file:
            return file.read()
    except FileNotFoundError:
        print(f"ファイルが見つかりません: {filename}")
    except PermissionError:
        print(f"ファイルアクセス権限がありません: {filename}")
    except UnicodeDecodeError:
        print(f"文字エンコーディングエラー: {filename}")
    except Exception as e:
        print(f"予期しないエラー: {e}")
    return None

# 使用例
content = safe_file_read('nonexistent.txt')
if content:
    print(content)

ロバストなファイル書き込み

import os
import tempfile

def safe_file_write(filename, content):
    """原子的な書き込み操作"""
    temp_file = None
    try:
        # 一時ファイルに書き込み
        with tempfile.NamedTemporaryFile(mode='w', delete=False, 
                                       dir=os.path.dirname(filename),
                                       encoding='utf-8') as temp_file:
            temp_file.write(content)
            temp_name = temp_file.name
        
        # 原子的な置換
        os.replace(temp_name, filename)
        return True
    
    except Exception as e:
        print(f"書き込みエラー: {e}")
        if temp_file and os.path.exists(temp_name):
            os.unlink(temp_name)
        return False

# 使用例
success = safe_file_write('important.txt', "重要なデータ")
print(f"書き込み{'成功' if success else '失敗'}")

ファイル操作の実用例

ログローテーション

import os
from datetime import datetime

class SimpleLogger:
    def __init__(self, log_file, max_size=1024*1024):  # 1MB
        self.log_file = log_file
        self.max_size = max_size
    
    def log(self, message):
        # ファイルサイズチェック
        if os.path.exists(self.log_file) and os.path.getsize(self.log_file) > self.max_size:
            self._rotate_log()
        
        # ログ書き込み
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        with open(self.log_file, 'a', encoding='utf-8') as file:
            file.write(f"[{timestamp}] {message}\n")
    
    def _rotate_log(self):
        backup_name = f"{self.log_file}.backup"
        if os.path.exists(backup_name):
            os.remove(backup_name)
        os.rename(self.log_file, backup_name)

# 使用例
logger = SimpleLogger('app.log')
logger.log("アプリケーション開始")
logger.log("データ処理完了")

設定ファイル管理

import json
import os

class ConfigManager:
    def __init__(self, config_file='config.json'):
        self.config_file = config_file
        self.config = self._load_config()
    
    def _load_config(self):
        if os.path.exists(self.config_file):
            try:
                with open(self.config_file, 'r', encoding='utf-8') as file:
                    return json.load(file)
            except json.JSONDecodeError:
                print("設定ファイルの形式が不正です")
        
        # デフォルト設定
        return {
            'database': {'host': 'localhost', 'port': 5432},
            'debug': False,
            'log_level': 'INFO'
        }
    
    def get(self, key, default=None):
        return self.config.get(key, default)
    
    def set(self, key, value):
        self.config[key] = value
        self._save_config()
    
    def _save_config(self):
        with open(self.config_file, 'w', encoding='utf-8') as file:
            json.dump(self.config, file, ensure_ascii=False, indent=2)

# 使用例
config = ConfigManager()
print(f"データベースホスト: {config.get('database', {}).get('host')}")
config.set('last_updated', '2024-01-01')

ファイル同期・バックアップ

import os
import shutil
from pathlib import Path

def backup_files(source_dir, backup_dir, extensions=None):
    """指定した拡張子のファイルをバックアップ"""
    source_path = Path(source_dir)
    backup_path = Path(backup_dir)
    
    # バックアップディレクトリ作成
    backup_path.mkdir(exist_ok=True)
    
    count = 0
    for file_path in source_path.rglob('*'):
        if file_path.is_file():
            # 拡張子フィルタ
            if extensions and file_path.suffix.lower() not in extensions:
                continue
            
            # 相対パスを維持してコピー
            relative_path = file_path.relative_to(source_path)
            dest_path = backup_path / relative_path
            dest_path.parent.mkdir(parents=True, exist_ok=True)
            
            shutil.copy2(file_path, dest_path)
            count += 1
    
    return count

# 使用例
# backed_up = backup_files('.', 'backup', ['.py', '.txt'])
# print(f"バックアップ完了: {backed_up}ファイル")

パフォーマンス最適化

バッファサイズの最適化

def efficient_file_copy(src, dst, buffer_size=65536):  # 64KB
    """効率的なファイルコピー"""
    with open(src, 'rb') as src_file:
        with open(dst, 'wb') as dst_file:
            while True:
                chunk = src_file.read(buffer_size)
                if not chunk:
                    break
                dst_file.write(chunk)

# バッファサイズによる性能比較
import time

def benchmark_copy(src, dst, buffer_sizes):
    for buffer_size in buffer_sizes:
        start = time.time()
        efficient_file_copy(src, f"{dst}_{buffer_size}", buffer_size)
        elapsed = time.time() - start
        print(f"バッファサイズ {buffer_size}: {elapsed:.3f}秒")

# 使用例(大きなファイルで実行)
# benchmark_copy('large_file.bin', 'copy', [1024, 8192, 65536])

メモリ効率的な大容量ファイル処理

def process_large_text_file(filename, process_func):
    """大容量テキストファイルの行ごと処理"""
    line_count = 0
    with open(filename, 'r', encoding='utf-8') as file:
        for line in file:
            process_func(line.strip())
            line_count += 1
            if line_count % 10000 == 0:
                print(f"処理済み: {line_count}行")
    return line_count

# 使用例
def count_words(line):
    return len(line.split())

# total_lines = process_large_text_file('huge_file.txt', count_words)

まとめ

Pythonでのファイル操作は、標準ライブラリの豊富な機能により様々なニーズに対応できます。適切な手法を選択することで、効率的で安全なファイル処理システムを構築できます。

重要なポイント:

  • with文を使った安全なファイル操作
  • pathlibでモダンなパス操作
  • 適切なエンコーディング指定(utf-8推奨)
  • 例外処理による堅牢なエラーハンドリング
  • チャンク読み込みで大容量ファイルに対応
  • 原子的操作で重要データの安全性確保

用途別の最適解:

  • テキストファイル: open() + with文
  • CSVファイル: csvモジュール
  • 設定ファイル: json/configparserモジュール
  • 大容量ファイル: チャンク処理
  • バイナリファイル: ‘rb’/’wb’モード

本記事のサンプルコードを参考に、あなたのプロジェクトに最適なファイル操作を実装してください。適切なエラーハンドリングとパフォーマンス最適化により、実用的なシステムを構築できます。

参考文献

  • Python公式ドキュメント – ファイルとディレクトリへのアクセス
  • pathlib公式ドキュメント
  • csv モジュール公式ドキュメント
  • tempfile モジュール公式ドキュメント

らくらくPython塾 – 読むだけでマスター

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座