C
Python/Files/Lesson 23

File I/O

45 min·theory
This chapter
1/2
Python

File I/O

🎯 After reading this lesson

By the end of this lesson, you will be able to confidently do the following 3 things.

  • pathlib.Path as the modern replacement for os.path
  • with open() context manager + explicit encoding
  • ✅ Using the csv, json, and yaml standard modules

Keep the learning objectives as a checklist and close the lesson once you can answer all of them.

6 File I/O Patterns — Code + Output

Reading and writing files = saving data to disk or loading it back. The foundation of every program.


1. Writing a Text File

python
# Create a new file and write to it
with open("memo.txt", "w", encoding="utf-8") as f:
    f.write("Hello
")
    f.write("Second line")

# File automatically closed — the core of the with statement

"w" = write (overwrites). Existing content is lost. "a" = append (adds to end).

⚠️ For non-ASCII text, encoding="utf-8" is required. Without it, the output will be garbled on Windows.


2. Reading a Text File

python
# Read all at once
with open("memo.txt", "r", encoding="utf-8") as f:
    content = f.read()
print(content)

# Line by line (saves memory for large files)
with open("memo.txt", "r", encoding="utf-8") as f:
    for line in f:
        print(line.rstrip())     # Remove 
 at the end of the line

# All lines as a list
with open("memo.txt", "r", encoding="utf-8") as f:
    lines = f.readlines()
print(lines)

3. The with Statement — Auto Close

python
# ❌ Old way — forgetting close() leads to memory leaks
f = open("memo.txt", "r", encoding="utf-8")
content = f.read()
f.close()                     # Easy to forget

# ✅ Modern way — with automatically closes
with open("memo.txt", "r", encoding="utf-8") as f:
    content = f.read()
# f.close() is automatically called when the block ends (even if an exception occurs)

with is the Python standard for resource management. Always use it.


4. Reading and Writing JSON

python
import json

# Python dict → JSON file
data = {"name": "Hong Gil-dong", "age": 28, "hobbies": ["reading", "coding"]}
with open("user.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

# JSON file → Python dict
with open("user.json", "r", encoding="utf-8") as f:
    loaded = json.load(f)
print(loaded["name"])         # Hong Gil-dong

ensure_ascii=False — saves non-ASCII characters as-is (without it, they become \uXXXX escapes).
indent=2 — pretty-prints for human readability.


5. Reading and Writing CSV

python
import csv

# Write
students = [
    ["Name", "Age", "Score"],
    ["Hong Gil-dong", 28, 85],
    ["Lee Mong-ryong", 30, 92],
]
with open("students.csv", "w", encoding="utf-8", newline="") as f:
    writer = csv.writer(f)
    writer.writerows(students)

# Read
with open("students.csv", "r", encoding="utf-8") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)
# ['Name', 'Age', 'Score']
# ['Hong Gil-dong', '28', '85']     ← All read as strings
# ['Lee Mong-ryong', '30', '92']

⚠️ newline="" — avoids extra blank line issues on Windows.


6. pathlib — Modern Path Handling (Python 3.6+)

python
from pathlib import Path

# Create path — OS independent (Windows / Mac / Linux)
path = Path("data") / "user.json"
print(path)                   # data\user.json (Win) or data/user.json (Mac)

# File exists?
if path.exists():
    content = path.read_text(encoding="utf-8")

# Write in one line
Path("hello.txt").write_text("Hello!", encoding="utf-8")

# Create folder (OK if already exists)
Path("data").mkdir(exist_ok=True)

os.path.join is the old way — pathlib is the modern recommended approach.


One-Line Summary

TaskCode
Writewith open(f, "w", encoding="utf-8") as f: f.write(s)
Readwith open(f, "r", encoding="utf-8") as f: s = f.read()
JSONjson.dump(data, f) / json.load(f)
CSVcsv.writer(f) / csv.reader(f)
PathPath("dir") / "file.txt"

Key takeaway: Always use with + encoding="utf-8". Without them, you risk garbled text or forgotten close() calls.

💻 Bad Example — `os.path` + Manual Close
import os

# os.path — low readability
base = '/data'
filepath = os.path.join(base, 'users', 'data.csv')  # Long and cumbersome

# encoding not specified, manual close
f = open(filepath)  # Platform default encoding (risky)
try:
    content = f.read()
finally:
    f.close()  # Manual management without with
💻 Good Example — `pathlib` + `with` Statement + `csv`/`json`
from pathlib import Path
import csv
import json
from typing import Generator

# pathlib — modern path handling
base = Path('/data')
filepath = base / 'users' / 'data.csv'  # Combine with / operator

print(filepath.name)        # 'data.csv'
print(filepath.stem)        # 'data'
print(filepath.suffix)      # '.csv'
print(filepath.parent)      # /data/users
print(filepath.exists())    # True/False

# Create directory
output_dir = Path('output') / 'reports'
output_dir.mkdir(parents=True, exist_ok=True)  # Create intermediate paths as well

# Read/write text files
config_path = Path('config.json')

if config_path.exists():
    text = config_path.read_text(encoding='utf-8')  # Read in one line
config_path.write_text('{"key": "value"}', encoding='utf-8')  # Write in one line

# Stream processing for large files
def process_large_log(filepath: Path) -> Generator[dict, None, None]:
    """Stream large log files line by line — memory O(1)"""
    with open(filepath, encoding='utf-8', errors='replace') as f:
        for line in f:  # Read line by line (does not load entire file)
            line = line.strip()
            if line and not line.startswith('#'):
                yield {'raw': line, 'length': len(line)}

# CSV processing
def read_users_csv(filepath: Path) -> list[dict]:
    with open(filepath, encoding='utf-8', newline='') as f:
        reader = csv.DictReader(f)
        return list(reader)

def write_users_csv(filepath: Path, users: list[dict]) -> None:
    with open(filepath, 'w', encoding='utf-8', newline='') as f:
        if users:
            writer = csv.DictWriter(f, fieldnames=users[0].keys())
            writer.writeheader()
            writer.writerows(users)

# JSON processing
def load_config(filepath: Path) -> dict:
    with open(filepath, encoding='utf-8') as f:
        return json.load(f)

def save_config(filepath: Path, config: dict) -> None:
    with open(filepath, 'w', encoding='utf-8') as f:
        json.dump(config, f, ensure_ascii=False, indent=2)

# Search files with glob
for csv_file in Path('data').glob('**/*.csv'):  # Recursive search
    print(csv_file)

🐍 Try It Yourself — File I/O

Run the concepts above as actual code. Changing the values and observing the results firsthand is the fastest way to learn.
✏️ Python 코드
📟 Console output
▶ Press the Run button
🐍 Real Python via Pyodide — first run takes 3–5s to load

🤖 Try Asking AI Like This

Knowing the concepts in this lesson lets you give AI specific instructions. Not a vague 'fix this' but a vocabulary-driven request — that is where token savings begin.

  • 'Migrate this os.path code to pathlib'
  • 'Add explicit encoding (encoding='utf-8') to this with open block'

Why This Reduces Tokens

When you don't know the concepts, you have to ask 'What does that mean?' after every response. That follow-up question is what burns tokens. Learn the concept once, and the conversation ends in a single turn.

File I/O - Python