Introduction to File Operations

File handling is essential for reading data from files and writing data to files. Python provides built-in functions to perform file operations easily.

Why File Handling?

  • Data Persistence: Save program data that survives after the program ends
  • Data Processing: Read and process data from external sources
  • Configuration: Store and read application settings
  • Logging: Record program activities and errors
  • Data Exchange: Share data between different programs

File Types

Type Description Examples
Text Files Human-readable, contain characters .txt, .csv, .html, .py
Binary Files Not human-readable, contain bytes .jpg, .pdf, .exe, .mp3

Opening Files

Use the open() function to open a file. It returns a file object that you can use to read or write.

open() Syntax

open_syntax.py
# Syntax: open(filename, mode)

# Basic examples
file = open("example.txt", "r")  # Open for reading
file = open("example.txt", "w")  # Open for writing
file = open("example.txt", "a")  # Open for appending

# Don't forget to close!
file.close()

File Modes

Mode Description File Exists? File Doesn't Exist?
'r' Read (default) Opens file Error
'w' Write (overwrites) Clears content Creates file
'a' Append Adds to end Creates file
'x' Exclusive create Error Creates file
'r+' Read and write Opens file Error
'w+' Write and read Clears content Creates file
'a+' Append and read Opens file Creates file
'b' Binary mode (add to other modes) 'rb', 'wb', 'ab'
file_modes.py
# Different file modes
import os

# Check if file exists before reading
if os.path.exists("data.txt"):
    file = open("data.txt", "r")
    content = file.read()
    file.close()
else:
    print("File not found!")

# Create new file (fails if exists)
try:
    file = open("new_file.txt", "x")
    file.write("This is a new file!")
    file.close()
except FileExistsError:
    print("File already exists!")

# Binary mode (for images, etc.)
image = open("photo.jpg", "rb")
data = image.read()
image.close()

# Text mode with encoding
file = open("unicode.txt", "r", encoding="utf-8")
content = file.read()
file.close()

Reading Files

Python provides several methods to read content from files.

Basic File Reading

reading_basic.py
# First, let's create a sample file
with open("sample.txt", "w") as f:
    f.write("Line 1: Hello World\n")
    f.write("Line 2: Python Programming\n")
    f.write("Line 3: File Operations\n")
    f.write("Line 4: Learning is fun!")

# Now read the file
file = open("sample.txt", "r")

# Method 1: Read entire file
content = file.read()
print("Entire file:")
print(content)

# Important: Close the file when done
file.close()

# Or reset position to read again
file = open("sample.txt", "r")
print("\nFirst 20 characters:")
print(file.read(20))  # Read first 20 characters
file.close()

Reading with for Loop

reading_loop.py
# Iterating through file lines (memory efficient)
file = open("sample.txt", "r")

print("Reading line by line:")
for line in file:
    print(line, end="")  # Lines already have \n

file.close()

# With line numbers
print("\n\nWith line numbers:")
file = open("sample.txt", "r")
for line_num, line in enumerate(file, 1):
    print(f"{line_num}: {line}", end="")

file.close()

Understanding Read Methods

read() Method

Reads the entire file or specified number of characters.

read_method.py
# read() - Read entire file or specified chars
file = open("sample.txt", "r")

# Read entire file
all_content = file.read()
print("All content:")
print(all_content)
print("-" * 30)

file.close()

# Read specific number of characters
file = open("sample.txt", "r")
chunk1 = file.read(10)  # First 10 chars
chunk2 = file.read(10)  # Next 10 chars
chunk3 = file.read()    # Rest of file

print(f"Chunk 1: '{chunk1}'")
print(f"Chunk 2: '{chunk2}'")
print(f"Chunk 3: '{chunk3}'")

file.close()

# Reading in chunks (for large files)
file = open("sample.txt", "r")
chunk_size = 20

print("\nReading in chunks:")
while True:
    chunk = file.read(chunk_size)
    if not chunk:  # Empty string = end of file
        break
    print(f"[{chunk}]")

file.close()

readline() Method

Reads one line at a time.

readline_method.py
# readline() - Read one line at a time
file = open("sample.txt", "r")

# Read first line
line1 = file.readline()
print(f"Line 1: {line1}", end="")

# Read second line
line2 = file.readline()
print(f"Line 2: {line2}", end="")

# Read third line
line3 = file.readline()
print(f"Line 3: {line3}", end="")

file.close()

# Reading all lines with readline()
print("\nReading all lines with while loop:")
file = open("sample.txt", "r")

line_num = 1
while True:
    line = file.readline()
    if not line:  # Empty string = end of file
        break
    print(f"{line_num}: {line.strip()}")  # strip() removes \n
    line_num += 1

file.close()

# Read partial line
file = open("sample.txt", "r")
partial = file.readline(10)  # Read up to 10 chars from line
print(f"\nPartial line: '{partial}'")

readlines() Method

Reads all lines and returns a list.

readlines_method.py
# readlines() - Read all lines into a list
file = open("sample.txt", "r")

lines = file.readlines()
print("Type:", type(lines))
print("Lines:", lines)

file.close()

# Process each line
print("\nProcessing each line:")
file = open("sample.txt", "r")
lines = file.readlines()

for i, line in enumerate(lines, 1):
    clean_line = line.strip()  # Remove \n and whitespace
    print(f"{i}. {clean_line}")

file.close()

# Access specific lines
print("\nAccessing specific lines:")
file = open("sample.txt", "r")
lines = file.readlines()

print(f"First line: {lines[0].strip()}")
print(f"Last line: {lines[-1].strip()}")
print(f"Number of lines: {len(lines)}")

file.close()

Comparison of Read Methods

Method Returns Memory Usage Best For
read() Entire file as string High (loads all) Small files, need all content
readline() One line as string Low (one line) Processing line by line
readlines() List of all lines High (loads all) Need random line access
for line in file Line iterator Low (one line) Large files

Best Practice

For large files, use for line in file or readline() to avoid loading the entire file into memory.

Writing Files

Python provides methods to write content to files.

Basic Writing

writing_basic.py
# Writing with 'w' mode (overwrites existing content)
file = open("output.txt", "w")
file.write("Hello, World!\n")
file.write("This is line 2.\n")
file.write("Python file operations are easy!")
file.close()

# Verify by reading
file = open("output.txt", "r")
print(file.read())
file.close()

# Writing over the same file (content replaced)
file = open("output.txt", "w")
file.write("New content - old content is gone!")
file.close()

# Verify
file = open("output.txt", "r")
print("\nAfter overwrite:")
print(file.read())
file.close()

Appending to Files

appending.py
# Create initial file
file = open("log.txt", "w")
file.write("Log started\n")
file.write("Entry 1: Application started\n")
file.close()

# Append new entries
file = open("log.txt", "a")
file.write("Entry 2: User logged in\n")
file.write("Entry 3: Data processed\n")
file.close()

# Append more
file = open("log.txt", "a")
file.write("Entry 4: User logged out\n")
file.close()

# Read final content
file = open("log.txt", "r")
print("Full log:")
print(file.read())
file.close()

Understanding Write Methods

write() Method

Writes a string to the file.

write_method.py
# write() - Write string to file
file = open("write_demo.txt", "w")

# Write returns number of characters written
chars = file.write("Hello, World!")
print(f"Characters written: {chars}")  # 13

# Must include \n for new lines
file.write("\n")
file.write("Second line\n")
file.write("Third line")

file.close()

# Using formatted strings
file = open("formatted.txt", "w")

name = "Alice"
age = 25
score = 95.5

file.write(f"Name: {name}\n")
file.write(f"Age: {age}\n")
file.write(f"Score: {score:.2f}\n")

file.close()

# Verify
with open("formatted.txt", "r") as f:
    print("\nFormatted output:")
    print(f.read())

writelines() Method

Writes a list of strings to the file.

writelines_method.py
# writelines() - Write list of strings
lines = [
    "Line 1: First line\n",
    "Line 2: Second line\n",
    "Line 3: Third line\n"
]

file = open("lines.txt", "w")
file.writelines(lines)
file.close()

# Note: writelines() doesn't add newlines automatically!
# Wrong way:
wrong_lines = ["One", "Two", "Three"]
file = open("no_newlines.txt", "w")
file.writelines(wrong_lines)
file.close()

# Check result
file = open("no_newlines.txt", "r")
print("Without newlines:")
print(file.read())  # OneTwoThree
file.close()

# Correct way - add newlines
correct_lines = [line + "\n" for line in ["One", "Two", "Three"]]
file = open("with_newlines.txt", "w")
file.writelines(correct_lines)
file.close()

# Or use join
file = open("joined.txt", "w")
file.write("\n".join(["One", "Two", "Three"]))
file.close()

# Verify
file = open("with_newlines.txt", "r")
print("\nWith newlines:")
print(file.read())
file.close()

Comparison of Write Methods

Method Input Adds Newlines? Returns
write() Single string No Number of chars written
writelines() List of strings No None

File Pointer & seek()

The file pointer tracks your current position in the file. Use seek() to move it and tell() to find it.

Understanding File Pointer

file_pointer.py
# Create a sample file
with open("pointer_demo.txt", "w") as f:
    f.write("ABCDEFGHIJKLMNOPQRSTUVWXYZ")

# Open and track pointer position
file = open("pointer_demo.txt", "r")

# tell() - get current position
print(f"Initial position: {file.tell()}")  # 0

# Read some characters
data = file.read(5)
print(f"Read: {data}")
print(f"Position after reading 5 chars: {file.tell()}")  # 5

data = file.read(5)
print(f"Read: {data}")
print(f"Position after reading 5 more: {file.tell()}")  # 10

file.close()

seek() Method

seek(offset, whence) moves the file pointer.

whence Value Meaning Example
0 (default) From beginning seek(10) - go to position 10
1 From current position seek(5, 1) - move 5 forward
2 From end seek(-10, 2) - 10 before end
seek_examples.py
# seek() examples
file = open("pointer_demo.txt", "r")

# Read first 5 characters
print(file.read(5))  # ABCDE

# Go back to beginning
file.seek(0)
print(file.read(5))  # ABCDE (again!)

# Go to position 10
file.seek(10)
print(f"Position: {file.tell()}")  # 10
print(file.read(5))  # KLMNO

# Go to specific position
file.seek(20)
print(file.read())  # UVWXYZ

file.close()

# Seek from end (requires binary mode for non-zero whence)
file = open("pointer_demo.txt", "rb")

# Go to 5 characters before end
file.seek(-5, 2)  # 2 = from end
print(file.read().decode())  # VWXYZ

# Go to current position + offset
file.seek(10, 0)  # Go to position 10
file.seek(5, 1)   # Move 5 forward (now at 15)
print(file.tell())  # 15

file.close()

Practical Uses of seek()

seek_practical.py
# Use case 1: Re-read file without reopening
file = open("sample.txt", "r")

# First read
print("First read:")
print(file.read())

# Reset to beginning
file.seek(0)

# Second read
print("\nSecond read:")
print(file.read())

file.close()

# Use case 2: Read specific section
with open("pointer_demo.txt", "r") as f:
    # Read characters 5-15
    f.seek(5)
    section = f.read(10)
    print(f"Characters 5-15: {section}")

# Use case 3: Modify part of file (r+ mode)
with open("modify.txt", "w") as f:
    f.write("Hello World!")

with open("modify.txt", "r+") as f:
    f.seek(6)  # Go to position 6
    f.write("Python")  # Overwrites "World!"

with open("modify.txt", "r") as f:
    print(f"Modified: {f.read()}")  # Hello Python

# Use case 4: Get file size
with open("pointer_demo.txt", "rb") as f:
    f.seek(0, 2)  # Go to end
    size = f.tell()
    print(f"File size: {size} bytes")

Context Managers (with statement)

The with statement ensures files are properly closed, even if errors occur.

Best Practice

Always use with when working with files. It automatically handles closing the file and is cleaner code.

context_manager.py
# Without context manager (manual close)
file = open("example.txt", "w")
try:
    file.write("Hello, World!")
finally:
    file.close()  # Must remember to close!

# With context manager (recommended!)
with open("example.txt", "w") as file:
    file.write("Hello, World!")
# File is automatically closed here

# Multiple files at once
with open("input.txt", "r") as infile, open("output.txt", "w") as outfile:
    content = infile.read()
    outfile.write(content.upper())

# File is closed automatically even if error occurs
try:
    with open("test.txt", "w") as f:
        f.write("Some data")
        # Even if error happens here...
        raise ValueError("Something went wrong!")
except ValueError as e:
    print(f"Error: {e}")
# File is still properly closed!

# Verify file is closed
with open("test.txt", "r") as f:
    pass  # Just open and close

print(f"File closed? {f.closed}")  # True

Reading and Writing Patterns

patterns.py
# Pattern 1: Read entire file
with open("sample.txt", "r") as f:
    content = f.read()
    print(content)

# Pattern 2: Read line by line
with open("sample.txt", "r") as f:
    for line in f:
        print(line.strip())

# Pattern 3: Read into list
with open("sample.txt", "r") as f:
    lines = f.readlines()

# Pattern 4: Write list to file
data = ["Item 1", "Item 2", "Item 3"]
with open("items.txt", "w") as f:
    for item in data:
        f.write(f"{item}\n")

# Pattern 5: Copy file
with open("source.txt", "r") as src, open("dest.txt", "w") as dst:
    dst.write(src.read())

# Pattern 6: Append timestamp
from datetime import datetime
with open("log.txt", "a") as f:
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    f.write(f"[{timestamp}] Event occurred\n")

Practical Programming Examples

Example 1: Word Counter

word_counter.py
# Count words, lines, and characters in a file

def analyze_file(filename):
    """Analyze a text file and return statistics."""
    try:
        with open(filename, "r") as f:
            content = f.read()
            
            # Count lines
            lines = content.split("\n")
            line_count = len(lines)
            
            # Count words
            words = content.split()
            word_count = len(words)
            
            # Count characters (with and without spaces)
            char_count = len(content)
            char_no_space = len(content.replace(" ", "").replace("\n", ""))
            
            return {
                "lines": line_count,
                "words": word_count,
                "characters": char_count,
                "characters_no_spaces": char_no_space
            }
    except FileNotFoundError:
        return None

# Create a sample file
with open("article.txt", "w") as f:
    f.write("Python is a powerful programming language.\n")
    f.write("It is used for web development, data science, and more.\n")
    f.write("Learning Python is fun and rewarding!")

# Analyze the file
stats = analyze_file("article.txt")
if stats:
    print("File Statistics:")
    print(f"  Lines: {stats['lines']}")
    print(f"  Words: {stats['words']}")
    print(f"  Characters: {stats['characters']}")
    print(f"  Characters (no spaces): {stats['characters_no_spaces']}")

Example 2: CSV File Handler

csv_handler.py
# Simple CSV file operations without csv module

def write_csv(filename, headers, data):
    """Write data to CSV file."""
    with open(filename, "w") as f:
        # Write headers
        f.write(",".join(headers) + "\n")
        
        # Write data rows
        for row in data:
            f.write(",".join(str(item) for item in row) + "\n")
    
    print(f"Data written to {filename}")

def read_csv(filename):
    """Read CSV file and return list of dictionaries."""
    with open(filename, "r") as f:
        lines = f.readlines()
        
        if not lines:
            return []
        
        # First line is headers
        headers = lines[0].strip().split(",")
        
        # Parse data rows
        data = []
        for line in lines[1:]:
            values = line.strip().split(",")
            row_dict = dict(zip(headers, values))
            data.append(row_dict)
        
        return data

# Create sample data
headers = ["Name", "Age", "City"]
students = [
    ["Alice", 20, "New York"],
    ["Bob", 22, "Los Angeles"],
    ["Charlie", 21, "Chicago"]
]

# Write to CSV
write_csv("students.csv", headers, students)

# Read from CSV
data = read_csv("students.csv")
print("\nStudent Data:")
for student in data:
    print(f"  {student['Name']}, {student['Age']}, {student['City']}")

Example 3: Log File Manager

log_manager.py
# Simple logging system using file operations
from datetime import datetime

class SimpleLogger:
    def __init__(self, filename):
        self.filename = filename
        # Create file if doesn't exist
        with open(filename, "a") as f:
            pass
    
    def log(self, level, message):
        """Write a log entry."""
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        entry = f"[{timestamp}] [{level.upper()}] {message}\n"
        
        with open(self.filename, "a") as f:
            f.write(entry)
    
    def info(self, message):
        self.log("INFO", message)
    
    def warning(self, message):
        self.log("WARNING", message)
    
    def error(self, message):
        self.log("ERROR", message)
    
    def get_logs(self, level=None):
        """Read and filter log entries."""
        with open(self.filename, "r") as f:
            logs = f.readlines()
        
        if level:
            logs = [log for log in logs if f"[{level.upper()}]" in log]
        
        return logs
    
    def clear_logs(self):
        """Clear all logs."""
        with open(self.filename, "w") as f:
            pass

# Usage
logger = SimpleLogger("app.log")

logger.info("Application started")
logger.info("Loading configuration...")
logger.warning("Config file not found, using defaults")
logger.info("Server listening on port 8080")
logger.error("Connection refused from 192.168.1.1")

# Display all logs
print("All Logs:")
for log in logger.get_logs():
    print(log, end="")

# Display only errors
print("\nErrors Only:")
for log in logger.get_logs("ERROR"):
    print(log, end="")

Example 4: Find and Replace in File

find_replace.py
# Find and replace text in a file

def find_and_replace(filename, find_text, replace_text):
    """Replace all occurrences of find_text with replace_text."""
    
    # Read original content
    with open(filename, "r") as f:
        content = f.read()
    
    # Count occurrences
    count = content.count(find_text)
    
    if count == 0:
        print(f"'{find_text}' not found in file.")
        return 0
    
    # Replace text
    new_content = content.replace(find_text, replace_text)
    
    # Write back
    with open(filename, "w") as f:
        f.write(new_content)
    
    print(f"Replaced {count} occurrence(s) of '{find_text}' with '{replace_text}'")
    return count

# Create sample file
with open("document.txt", "w") as f:
    f.write("Hello World! Hello Python! Hello everyone!\n")
    f.write("The world is beautiful. Hello again!")

print("Original content:")
with open("document.txt", "r") as f:
    print(f.read())

# Replace "Hello" with "Hi"
find_and_replace("document.txt", "Hello", "Hi")

print("\nAfter replacement:")
with open("document.txt", "r") as f:
    print(f.read())

Example 5: File Backup System

backup_system.py
# Simple file backup system
import os
from datetime import datetime

def create_backup(source_file):
    """Create a timestamped backup of a file."""
    
    # Check if source exists
    if not os.path.exists(source_file):
        print(f"Error: '{source_file}' not found!")
        return None
    
    # Create backup filename with timestamp
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    name, ext = os.path.splitext(source_file)
    backup_file = f"{name}_backup_{timestamp}{ext}"
    
    # Copy file
    with open(source_file, "r") as src:
        content = src.read()
    
    with open(backup_file, "w") as dst:
        dst.write(content)
    
    print(f"Backup created: {backup_file}")
    return backup_file

def restore_backup(backup_file, target_file):
    """Restore a backup file."""
    
    if not os.path.exists(backup_file):
        print(f"Error: Backup '{backup_file}' not found!")
        return False
    
    with open(backup_file, "r") as src:
        content = src.read()
    
    with open(target_file, "w") as dst:
        dst.write(content)
    
    print(f"Restored '{backup_file}' to '{target_file}'")
    return True

# Demo
# Create original file
with open("important.txt", "w") as f:
    f.write("This is important data!\n")
    f.write("Don't lose this information.")

# Create backup
backup = create_backup("important.txt")

# Simulate data loss (modify original)
with open("important.txt", "w") as f:
    f.write("Oops! Data was changed!")

print("\nAfter modification:")
with open("important.txt", "r") as f:
    print(f.read())

# Restore from backup
if backup:
    restore_backup(backup, "important.txt")
    
    print("\nAfter restore:")
    with open("important.txt", "r") as f:
        print(f.read())