【Python】ファイル操作まとめ

# はじめに

ファイル操作は、プログラムでデータの保存、読み込み、編集を行う際に欠かせない機能です。従来は `os` モジュールや `os.path` モジュールを使っていましたが、Python 3 以降では `pathlib.Path` を利用することで、コードがシンプルかつ可読性が高くなります。さらに、`tempfile` モジュールを使うことで、一時的なファイルやディレクトリの操作も容易に行えます。

# pathlib.Path を選ぶ理由

`os.path` モジュールは関数ベースの操作が中心ですが、`pathlib.Path` はパスをオブジェクトとして扱い、直感的なメソッドや演算子を提供します。たとえば、パスの結合は `/` 演算子で行えるため、コードがより簡潔になります。また、`with` 構文を用いてファイルの読み書きを明示的かつ安全に行うことができます。

# サンプルコード

以下のコード例では、指定した場所にファイルが存在するか確認し、存在しなければ新規作成して `"hello world!"` を with 文を使って書き込み、その後ファイルを読み込んで中身を出力します。

**コード例**

```python
from pathlib import Path

# 操作対象のファイルパス
file_path = Path("sample.txt")

# ファイルに "hello world!" を書き込み
with file_path.open("w", encoding="utf-8") as f:
    f.write("hello world!")

# ファイルの中身を読み込んで出力
with file_path.open("r", encoding="utf-8") as f:
    content = f.read()
print(content)
```

**出力例**

```plaintext
hello world!
```

## パス操作の基本：exists, mkdir, touch

`pathlib.Path` には、ファイルやディレクトリの存在確認・作成に便利なメソッドが用意されています。

- **exists()**  
  指定したパスが存在するかを確認します。

- **mkdir()**  
  新しいディレクトリを作成します。引数に `parents=True` や `exist_ok=True` を指定することで、階層構造も簡単に作成できます。

- **touch()**  
  空のファイルを作成します。すでに存在する場合は、更新日時を変更する動作をします。

**コード例**

```python
from pathlib import Path

# 新しく作成するディレクトリのパス
dir_path = Path("new_dir")

# ディレクトリが存在しない場合は作成
if not dir_path.exists():
    dir_path.mkdir()
print(f"ディレクトリ '{dir_path}' の作成に成功しました")

# 新しいファイルをディレクトリ内に作成
file_in_dir = dir_path / "example.txt"
file_in_dir.touch()
print(f"ファイル '{file_in_dir}' を作成しました")
```

**出力例**

```plaintext
ディレクトリ 'new_dir' の作成に成功しました
ファイル 'new_dir/example.txt' を作成しました
```

## 安全なファイル操作：with文によるopenメソッドの使い方

`Path` オブジェクトは、標準の `open()` と同様のメソッドを持ち、`with` 構文と組み合わせることでファイルの安全な開閉が可能です。  
主な引数は以下の通りです。

- **mode**  
  開くモード。`"w"`（書き込み）、`"r"`（読み込み）、`"a"`（追記）などがあります。

- **encoding**  
  テキストファイルの場合、使用する文字コードを指定します。例：`"utf-8"`。

**コード例**

```python
from pathlib import Path

# 書き込み用のファイルパス
file_path = Path("sample2.txt")

# with 構文を使ってファイルを開き、テキストを書き込む
with file_path.open(mode="w", encoding="utf-8") as f:
    f.write("Python file operations are fun!")

# with 構文を使ってファイルを再度開き、内容を読み込む
with file_path.open(mode="r", encoding="utf-8") as f:
    print(f.read())
```

**出力例**

```plaintext
Python file operations are fun!
```

## 複数行テキストの読み書きとファイルオブジェクトの基本

以下の例では、複数行の文字列をラインごとに書き込み・読み込みを行う方法を紹介します。

**コード例**

```python
from pathlib import Path

# ファイルパスの指定
file_path = Path("sample3_line.txt")

# 書き込みたい各行をリストで定義（各行の末尾に改行文字を付与）
lines = ["First line\n", "Second line\n", "Third line\n"]

# 行ごとに書き込み
with file_path.open("w", encoding="utf-8") as f:
    for line in lines:
        f.write(line)

print("ファイルへの書き込み完了。")

# 行ごとに逐次読み込み
with file_path.open("r", encoding="utf-8") as f:
    lines = f.readlines()
    for line in lines:
        # 改行コードを除去して出力
        print("読み込んだ行:", line.rstrip())
```

**出力例**

```plaintext
ファイルへの書き込み完了。
読み込んだ行: First line
読み込んだ行: Second line
読み込んだ行: Third line
```

# CSV, JSON, YAML ファイルの読み書き

ここでは、CSV、JSON、YAML といったファイル形式にデータを書き込み、読み込む方法を紹介します。いずれの例でも、組み込みの `open` 関数の代わりに `Path.open` を使用しています。  
※ YAML を扱う場合は `PyYAML` のインストールが必要です（`pip install pyyaml`）。

**コード例（CSV ファイル）**

```python
import csv
from pathlib import Path

# サンプルデータ
data = [
    ["name", "age"],
    ["Alice", 30],
    ["Bob", 25]
]

# CSV ファイルに書き込み
csv_path = Path("sample.csv")
with csv_path.open("w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerows(data)

# CSV ファイルを読み込んで内容を表示
with csv_path.open("r", encoding="utf-8") as f:
    print(f.read())
```

**出力例（CSV ファイル）**

```plaintext
name,age
Alice,30
Bob,25
```

**コード例（JSON ファイル）**

```python
import json
from pathlib import Path

# サンプルデータ
data = {
    "name": "Alice",
    "age": 30,
    "hobbies": ["reading", "swimming"]
}

# JSON ファイルに書き込み
json_path = Path("sample.json")
with json_path.open("w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=4)

# JSON ファイルを読み込んで内容を表示
with json_path.open("r", encoding="utf-8") as f:
    content = json.load(f)
    print(content)
```

**出力例（JSON ファイル）**

```plaintext
{'age': 25, 'languages': ['Python', 'JavaScript'], 'name': 'Bob'}
```

**コード例（YAML ファイル）**

```python
import yaml
from pathlib import Path

# サンプルデータ
data = {
    "name": "Bob",
    "age": 25,
    "languages": ["Python", "JavaScript"]
}

# YAML ファイルに書き込み
yaml_path = Path("sample.yaml")
with yaml_path.open("w", encoding="utf-8") as f:
    yaml.dump(data, f, allow_unicode=True)

# YAML ファイルを読み込んで内容を表示
with yaml_path.open("r", encoding="utf-8") as f:
    content = yaml.safe_load(f)
    print(content)
```

**出力例（YAML ファイル）**

```plaintext
{
    "name": "Alice",
    "age": 30,
    "hobbies": [
        "reading",
        "swimming"
    ]
}
```

# 一時ファイルと一時ディレクトリの活用

Python の `tempfile` モジュールは、一時的なファイルやディレクトリの作成を容易にします。以下では、`TemporaryFile`、`NamedTemporaryFile`、`TemporaryDirectory` の使い方を紹介します。

## 1. TemporaryFile の使い方

`TemporaryFile` は一時ファイルを作成し、with ブロックを抜けると自動的に削除されます。書き込みモードと読み込みモードを同時に使う場合、`mode="w+t"` のように指定します。

**コード例**

```python
import tempfile

with tempfile.TemporaryFile(mode="w+t", encoding="utf-8") as temp_file:
    # 一時ファイルにデータを書き込む
    temp_file.write("Temporary data")
    # ファイルポインタを先頭に戻す
    temp_file.seek(0)
    # 書き込んだ内容を読み出して出力する
    print(temp_file.read())
```

**出力例**

```plaintext
Temporary data
```

## 2. NamedTemporaryFile の使い方

以下の例では、`NamedTemporaryFile` により一時ファイルを作成し、そのファイルパスを `Path` オブジェクトに変換して、`Path.open` を用いた書き込み・読み込みを行います。

**コード例**

```python
import tempfile
from pathlib import Path
import os

with tempfile.NamedTemporaryFile(mode="w+t", delete=True, encoding="utf-8") as named_temp:
    # 一時ファイルのパスを Path 化
    named_temp_path = Path(named_temp.name)
    
    # with ブロック内で Path.open を使い、書き込み
    with named_temp_path.open("w", encoding="utf-8") as f:
        f.write("Named temporary file data")
    
    # 別の with ブロックで読み込み
    with named_temp_path.open("r", encoding="utf-8") as f:
        data = f.read()
        print(data)

# with ブロック終了後、ファイルは自動削除される
print("ファイルが存在するか？", os.path.exists(named_temp_path))
```

**出力例**

```plaintext
Named temporary file data
ファイルが存在するか？ False
```

Linux環境だと正しく実行できましたが、Windows環境でこれをやるとPermissionErrorで怒られました。NamedTemporaryFileを使うよりも、後述のTemporaryDirectoryを使い、tmp_dir内でファイルを作成するようにした方が良さそうです。

## 3. TemporaryDirectory の使い方

`TemporaryDirectory` は一時ディレクトリを作成し、そのディレクトリパスを取得します。このディレクトリ内でファイル操作を行い、with ブロックを抜けるとディレクトリごと自動的に削除されます。

**コード例**

```python
import tempfile
from pathlib import Path
import os

with tempfile.TemporaryDirectory() as temp_dir:
    temp_dir_path = Path(temp_dir)
    print(f"Temporary directory: {temp_dir_path}")
    
    # 一時ディレクトリ内にファイルを作成して書き込み
    file_path = temp_dir_path / "tempfile.txt"
    with file_path.open("w", encoding="utf-8") as f:
        f.write("Data in temporary directory")
    
    # 作成したファイルの内容を読み出し
    with file_path.open("r", encoding="utf-8") as f:
        print(f.read())

# TemporaryDirectory はコンテキスト終了後に削除されるため、temp_dir は存在しない
print("Temporary directory exists?", os.path.exists(temp_dir_path))
```

**出力例**

```plaintext
Temporary directory: /tmp/tmp12345678   # パスは環境により異なる
Data in temporary directory
Temporary directory exists? False
```

# まとめ

本記事では、Python におけるファイル操作の基本について、`pathlib.Path` を中心に解説しました。また、with 文を用いた安全な読み書きの方法、CSV、JSON、YAML といった各種ファイル形式の入出力、さらに `tempfile` モジュールを使った一時ファイル・一時ディレクトリの活用方法について具体例を紹介しました。  
これらのサンプルコードと出力例を参考に、実際の開発でのファイル操作を効率的かつ安全に行っていただければ幸いです。

# 参考サイト

https://docs.python.org/ja/3.13/library/pathlib.html

https://docs.astral.sh/ruff/rules/builtin-open/

https://note.nkmk.me/python-pathlib-file-open-read-write-unlink/

https://qiita.com/yuji38kwmt/items/c6f50e1fc03dafdcdda0