Working with Files in Python: Modes, Reading, Writing, and Safe Editing

Opening and Closing Files

The open() function returns a file object and accepts a mode string that controls how the stream behaves. Common text modes are r, w, a, r+, w+, and a+. Binary modes append b, e.g., rb, wb. Always close a file when finished, preferably using a with statement.

with open("log.txt", "r", encoding="utf-8") as handle:
    data = handle.read()

Read Modes (r, rb)

Text Mode

with open("notes.txt", "r", encoding="utf-8") as handle:
    body = handle.read()
    print(body)

r mode needs a encoding matching the file’s actual byte representation. Without arguments, read() consumes the entire file.

Binary Mode

with open("notes.txt", "rb") as handle:
    raw = handle.read()
    print(raw)          # bytes object, e.g., b'hello'

Binary mode is essential for non-text formats such as images, videos, or MP3 files. No encoding is allowed.

Path Types

  • Absolute path – starts from the root directory, e.g., C:\Users\docs\data.csv.
  • Relative path – relative to the current working directory. Use .. for a parent folder. Relative paths make projects portable.

Incremental Reading

read() loads everything into memory. Larger files require a streamed approach.

with open("large_log.txt", "r", encoding="utf-8") as handle:
    chunk = handle.read(5)      # first 5 characters
    second_chunk = handle.read(5)
    print(chunk, second_chunk)

Positions advance automatically. Binary mode’s read(n) reads n bytes.

with open("large_log.txt", "rb") as handle:
    block = handle.read(4)
    print(block)                # b'\x48\x65\x6c\x6c'

readline() fetches a single line, including the trailing newline. Call strip() to remove it.

with open("lyrics.txt", "r", encoding="utf-8") as handle:
    first_line = handle.readline()
    second_line = handle.readline()
    print(first_line.strip(), second_line.strip())

readlines() builds a list of all lines—memory intensive.

Best practice for line-by-line processing:

with open("lyrics.txt", "r", encoding="utf-8") as handle:
    for line in handle:
        print(line.strip())

This idiom never loads the whole file into memory.

Write Modes (w, wb)

Write mode creates a new file or truncates a existing one before writing.

with open("output.txt", "w", encoding="utf-8") as handle:
    handle.write("First line\n")
    handle.write("Second line\n")

Attempting to read in w mode raises io.UnsupportedOperation.

Binary write requires encoded bytes:

with open("output.bin", "wb") as handle:
    handle.write("data payload".encode("utf-8"))

Append Modes (a, ab)

Append adds content at the end without removing existing data.

with open("journal.txt", "a", encoding="utf-8") as handle:
    handle.write("\nNew entry")

Read‑Write Modes (r+, w+, a+)

r+ opens for reading and writing. Read first, then write; otherwise the cursor position may overwrite initial content.

with open("data.txt", "r+", encoding="utf-8") as handle:
    existing = handle.read()
    handle.write(" appended text")
    print(existing)

w+ truncates immediately and read returns empty data. Rarely useful.

a+ allows reading and appending; reading initially returns nothing because the pointer is at the end.

Cursor Control

seek(offset, whence) moves the internal pointer. whence can be:

  • 0 – start (default)
  • 1 – current position
  • 2 – end

For UTF‑8 text, offsets must align to character boundaries (multiples of 3 for many Chinese characters).

with open("entries.txt", "r+", encoding="utf-8") as handle:
    handle.seek(0)          # move to start
    text = handle.read()
    handle.seek(0, 2)       # move to end
    handle.write("\nFooter")
    handle.seek(0)          # move back to start
    print(handle.read())

tell() reports the current byte offset.

with open("entries.txt", "r+", encoding="utf-8") as handle:
    handle.write("abc")
    print(handle.tell())    # 3

truncate(size=None) resizes the file. Without size, it cuts at the current pointer. With size, it keeps the first size bytes.

with open("draft.txt", "r+", encoding="utf-8") as handle:
    handle.seek(10)
    handle.truncate()       # keep only first 10 bytes from start

Important caveat: After any read in r+ mode, subsequent writes append at the end regardless of seek() calls. To truncate reliably, move the cursor first, truncate, then write only when necessary.

Safe In‑Place File Editing

Direct mid‑file modifications risk data loss. The standard pattern reads, modifies, and swaps files.

import os

source = "original.txt"
temp = "original.tmp"

with open(source, "r", encoding="utf-8") as f_in, \
     open(temp, "w", encoding="utf-8") as f_out:
    for line in f_in:
        updated = line.replace("old_term", "new_term")
        f_out.write(updated)

os.remove(source)
os.rename(temp, source)

This streaming approach handles files of any size safely and uses with to ensure clean closure.

Tags: python File Handling Input/Output encoding binary mode

Posted on Wed, 29 Jul 2026 16:51:55 +0000 by PHPisFUN