Random File Access in Python: Repositioning Streams with seek()

The file.seek(offset, whence=os.SEEK_SET) method moves the file stream pointer to an arbitrary byte position, enabling non-sequential random read and write access across files.

Text vs. Binary Rules: In binary mode ('rb', 'r+b'), Python supports arbitrary forward and backward seeks from the start, current cursor, or end of file. In text mode ('r'), relative seeks are strictly forbidden by CPython due to multi-byte encoding boundaries.

1. Method Syntax & Signature 🔝

The standard CPython signature for file.seek() is:

file_object.seek(offset, whence=0)
Parameter Type Default Behavior & Description
offset int Required Number of bytes to move the cursor. Can be positive (forward) or negative (backward, in binary mode).
whence int 0 (os.SEEK_SET) Reference point for the movement: 0 = file start, 1 = current pointer, 2 = file end.
Return Value int — The new absolute byte offset from the start of the file.

2. The Whence Parameter & os Constants 🔝

Python provides symbolic constants in the standard os module that enhance code readability when specifying the reference point:

Integer os Constant Reference Anchor Permitted Offset Values
0 os.SEEK_SET Beginning of the file (offset 0) ≥ 0 (positive integers or 0)
1 os.SEEK_CUR Current file pointer location Positive (forward) or negative (backward) in binary mode
2 os.SEEK_END End of the file (EOF) ≤ 0 (negative integers to seek backward from EOF)

3. Text Mode Constraints in Python 3 🔝

Because modern text encodings like UTF-8 use variable-length byte representations (1 to 4 bytes per character), jumping to an arbitrary byte offset in text mode can split a multi-byte Unicode sequence in half. Therefore, Python 3 enforces strict rules for text files ('r', 'w', 'a'):

  • Allowed: seek(0) — Rewinding to the very beginning of the file.
  • Allowed: seek(0, os.SEEK_END) — Jumping to the exact end of the file.
  • Allowed: seek(saved_offset) — Passing an opaque integer returned earlier by tell().
  • Forbidden: Non-zero relative seeks (seek(5, 1) or seek(-10, 2)) — Raises io.UnsupportedOperation: can't do nonzero cur-relative seeks.
Text Mode Seeking Restrictions
import io

with open("document.txt", "r", encoding="utf-8") as f:
    # Valid: Rewind to start
    f.seek(0)
    
    # Valid: Jump to end of file
    f.seek(0, 2)
    
    # Invalid in text mode: Raises UnsupportedOperation
    try:
        f.seek(5, 1)
    except io.UnsupportedOperation as err:
        print("Relative seek forbidden in text mode:", err)

4. Binary Mode Operations ('rb' and 'r+b') 🔝

In binary mode, Python treats files as contiguous arrays of raw bytes without encoding translation. All three whence anchor points and positive/negative offsets are fully supported:

Demonstration: Binary Navigation across Whence Points
import os

with open("payload.bin", "rb") as fob:
    # 1. SEEK_SET: Jump 10 bytes forward from start
    fob.seek(10, os.SEEK_SET)
    print("Byte at offset 10:", fob.read(4))
    
    # 2. SEEK_CUR: Skip 6 bytes forward from current location
    fob.seek(6, os.SEEK_CUR)
    print("Byte after relative skip:", fob.read(4))
    
    # 3. SEEK_END: Read last 8 bytes of the file
    fob.seek(-8, os.SEEK_END)
    print("Final 8 bytes of file:", fob.read(8))

5. Rewinding Streams with seek(0) 🔝

The most common production use case for seek() is resetting the read cursor after parsing, writing, or benchmarking, avoiding costly file re-opening:

Multi-Pass Stream Processing
with open("dataset.csv", "r", encoding="utf-8") as stream:
    # Pass 1: Count total rows
    row_count = sum(1 for _ in stream)
    print(f"Total records found: {row_count}")
    
    # Rewind cursor back to the start
    stream.seek(0)
    
    # Pass 2: Ingest header definition
    header = stream.readline().strip()
    print(f"Header columns: {header}")

6. Real-World Engineering Recipes 🔝

Recipe 1: Reading Tail Bytes of Large Files

Inspect the trailer or recent log entries from a multi-gigabyte file in milliseconds by seeking backward from EOF:

import os

def read_file_tail(filepath: str, num_bytes: int = 1024) -> bytes:
    file_size = os.path.getsize(filepath)
    seek_offset = min(num_bytes, file_size)
    with open(filepath, "rb") as f:
        f.seek(-seek_offset, os.SEEK_END)
        return f.read(seek_offset)

recent_bytes = read_file_tail("server.log", 256)
print("Tail log buffer loaded instantly without reading whole file.")

Recipe 2: In-Place Binary Patching with 'r+b'

Update specific fields in fixed-width binary databases or header files without rewriting the entire dataset:

def patch_header_flag(filepath: str, byte_offset: int, new_value: bytes) -> None:
    with open(filepath, "r+b") as bin_stream:
        bin_stream.seek(byte_offset, 0)
        bin_stream.write(new_value)

patch_header_flag("firmware.bin", 4, b"\x01")
print("Header flag patched in-place at byte offset 4.")

7. Common Pitfalls & Boundary Traps 🔝

Trap 1: Seeking in Append Mode ('a') Does Not Change Write Location

Under POSIX and C runtime specifications, files opened in append mode ('a' or 'a+') forcibly redirect all write operations to the end of the file (EOF) regardless of previous seek() invocations. To modify files in-place, use mode 'r+' or 'r+b'.

Trap 2: Seeking Beyond EOF (Sparse Files)

Seeking past the end of a file and writing creates a sparse file filled with zero bytes (\x00) between the old EOF and the new write offset. Verify file boundaries before arbitrary seeks if null padding is undesirable.

8. Production Test Suite & Self-Checking Assertions 🔝

Run this complete test harness verifying binary seeks, relative cursor jumps, and tail offset reads:

SeekVerifier Test Harness
import tempfile
import os

class SeekVerifier:
    @staticmethod
    def read_span(path: str, offset: int, length: int) -> bytes:
        with open(path, "rb") as stream:
            stream.seek(offset, os.SEEK_SET)
            return stream.read(length)

# Verification unit assertions
with tempfile.TemporaryDirectory() as td:
    v = SeekVerifier()
    test_file = os.path.join(td, "verify.bin")
    raw_payload = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    with open(test_file, "wb") as f:
        f.write(raw_payload)
        
    # Test 1: Absolute seek from start (SEEK_SET)
    assert v.read_span(test_file, 10, 5) == b"ABCDE"
    
    # Test 2: Seek from end (SEEK_END)
    with open(test_file, "rb") as f:
        f.seek(-4, os.SEEK_END)
        assert f.read(4) == b"WXYZ"
        
    # Test 3: Relative seek (SEEK_CUR)
    with open(test_file, "rb") as f:
        f.seek(10)
        f.seek(5, os.SEEK_CUR)
        assert f.read(5) == b"FGHIJ"

print("All file.seek() unit assertions passed successfully!")

Practice in Google Colab & GitHub 🔝

Open in Google Colab View on GitHub

Run all file seek exercises interactively, test SEEK_SET, SEEK_CUR, and SEEK_END whence anchors, read log tails, and patch binary headers in Google Colab.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer