March 10, 2026 · All About CS

The Complete Guide to File Handling in Python

Master every file operation in Python — creating, writing, reading, appending, and deleting files. Includes the with statement, file pointers, the os module, and practical patterns for safe file I/O.

pythonfile-handlingioos-modulebeginners

The Complete Guide to File Handling in Python

Files are how programs remember things after they stop running. Every variable, every computed result, every piece of user input — it all vanishes from memory the moment your script finishes. File handling gives your programs a permanent memory: a way to store data on disk and retrieve it later.

This guide covers the full lifecycle — creating, writing, reading, appending, and deleting files — along with professional patterns like the with statement and defensive existence checks using the os module.

Why File Handling Matters

Think about a scenario: you write a program, run it, enter some data, perform operations, and get output. When the program stops, everything is gone — cleared from memory because it was all temporary.

But what if those values are important? What if you want to pick up where you left off next time you run the program?

You could use a database, but that adds complexity: schema design, connection management, query languages. For many use cases — logging, configuration, caching, simple data persistence — a text file is the simplest and most pragmatic solution.

When to use files vs. databases: Files are ideal for configuration, logging, small datasets, and data exchange between programs. Databases become necessary when you need structured queries, concurrent access, or datasets that grow beyond what fits comfortably in a single file.


The open() Function — Your Gateway to File Operations

Every file operation in Python starts with the built-in open() function. It takes two arguments: the file path and the mode that describes what you intend to do with the file.

Python
my_file = open("path/to/file.txt", "mode")
# ... perform read or write operations ...
my_file.close()

After every operation, you must close the file with .close(). Leaving files open can lock them, prevent other programs from accessing them, and cause data corruption.


File Modes Explained

The mode string tells Python exactly how you want to interact with the file:

ModeNameDescription
"r"ReadOpen an existing file for reading. Raises an error if the file doesn't exist.
"w"WriteOpen a file for writing. Creates the file if it doesn't exist. Overwrites existing content.
"a"AppendOpen a file for appending. Creates the file if it doesn't exist. Preserves existing content.
"x"CreateCreate a new file. Raises an error if the file already exists.
"r+"Read + WriteOpen for both reading and writing. Raises an error if the file doesn't exist.
"w+"Write + ReadOpen for writing and reading. Creates the file if it doesn't exist. Overwrites existing content.
"a+"Append + ReadOpen for appending and reading. Creates the file if it doesn't exist. File pointer starts at the end.

The r+ vs w+ distinction: Both allow reading and writing, but r+ requires the file to already exist (raises FileNotFoundError otherwise), while w+ creates the file if missing — and erases all existing content in the process.


Creating and Writing Files

The most common way to create a file and write data into it is using "w" mode:

Python
my_file = open("stored_files/program_data.txt", "w")
my_file.write("Hello There! Program data stored successfully!")
my_file.close()

After running this code, a file named program_data.txt appears inside the stored_files/ directory with the written text inside it. If the file already existed, its previous contents are completely replaced.

Writing Multiple Lines

Each call to .write() continues from where the previous one stopped — on the same line. To write on separate lines, insert the newline character \n:

Python
my_file = open("stored_files/program_data.txt", "w")
my_file.write("Line 1: Data stored successfully!\n")
my_file.write("Line 2: More data follows.\n")
my_file.write("Line 3: Final entry.")
my_file.close()

The resulting file:

Line 1: Data stored successfully!
Line 2: More data follows.
Line 3: Final entry.

Reading Files

To read an existing file, open it in "r" mode:

Python
my_file = open("stored_files/program_data.txt", "r")
content = my_file.read()
print(content)
my_file.close()

The .read() method returns the entire content of the file as a single string.

Reading Line by Line

For large files, reading everything at once isn't practical. The .readline() method reads one line at a time:

Python
my_file = open("stored_files/program_data.txt", "r")

first_line = my_file.readline()
second_line = my_file.readline()

print("Line 1:", first_line)
print("Line 2:", second_line)

my_file.close()

Each call to .readline() advances the internal file pointer to the next line. The first call returns line 1, the second call returns line 2, and so on.

Reading All Lines Into a List

The .readlines() method returns every line as an element in a list — useful when you want to process lines with a loop:

Python
my_file = open("stored_files/program_data.txt", "r")
lines = my_file.readlines()

for i, line in enumerate(lines, 1):
    print(f"Line {i}: {line.strip()}")

my_file.close()

Appending to Files

Append mode ("a") adds new content at the end of the file without touching existing data:

Python
my_file = open("stored_files/program_data.txt", "a")
my_file.write("\nAppended: Here goes some more data!")
my_file.close()

Write vs. Append — The Critical Difference

This distinction trips up every beginner at least once:

ModeExisting ContentFile Pointer Starts At
"w"Erased — completely overwrittenBeginning of file
"a"Preserved — new data added after itEnd of file

Run a program with "w" mode ten times, and you'll only see the output from the last run. Run it with "a" mode ten times, and you'll see all ten outputs stacked.

Python
# Demonstrating append behavior
for i in range(3):
    my_file = open("stored_files/counter.txt", "a")
    my_file.write(f"Run #{i + 1}\n")
    my_file.close()

# counter.txt now contains:
# Run #1
# Run #2
# Run #3

Deleting Files

Python's built-in open() function handles creation, reading, and writing — but not deletion. For that, you need the os module:

Python
import os

os.remove("stored_files/program_data.txt")

After this runs, the file is permanently deleted from disk.

Defensive Deletion — Avoiding FileNotFoundError

If the file doesn't exist, os.remove() raises a FileNotFoundError. Always check first:

Python
import os

file_path = "stored_files/program_data.txt"

if os.path.exists(file_path):
    os.remove(file_path)
    print(f"Deleted: {file_path}")
else:
    print(f"File not found: {file_path}")

This pattern applies to reading too. Before opening a file in "r" mode, use os.path.exists() to verify it's actually there. This prevents your program from crashing on missing files.


The with Statement — The Professional Way

Every example so far has manually called .close(). Forget that line once, and you risk data corruption or locked files. The with statement eliminates this risk entirely:

Python
with open("stored_files/program_data.txt", "w") as my_file:
    my_file.write("Line 1: Written safely.\n")
    my_file.write("Line 2: No need to call close().\n")

# File is automatically closed here, even if an error occurs inside the block

The with statement is a context manager — it guarantees the file is closed when the block ends, regardless of whether the code inside succeeds or raises an exception. This is the recommended pattern for all file operations in professional Python code.

Rewriting All Operations With with

Writing:

Python
with open("data.txt", "w") as f:
    f.write("Fresh content.\n")

Reading:

Python
with open("data.txt", "r") as f:
    content = f.read()
    print(content)

Appending:

Python
with open("data.txt", "a") as f:
    f.write("Appended safely.\n")

Understanding the File Pointer

The file pointer (also called the file handle) is an internal cursor that determines where the next read or write operation starts.

ModePointer Starts AtBehavior
"r"BeginningReads forward from start
"w"BeginningErases file, writes from start
"a"EndWrites after existing content
"r+"BeginningReads and writes from start
"a+"EndCan read and write, but pointer starts at end

This is why append mode preserves data and write mode overwrites it — the pointer starts in different positions, and write mode clears the file first.

When you call .readline(), the pointer advances to the next line. Call it again, and you get the next line — not the first one again. The pointer always moves forward.

Python
with open("data.txt", "r") as f:
    print(f.readline())   # → Line 1 (pointer moves to line 2)
    print(f.readline())   # → Line 2 (pointer moves to line 3)
    print(f.tell())       # → Current byte position of the pointer

You can also manually reset the pointer with .seek():

Python
with open("data.txt", "r") as f:
    content = f.read()      # Reads entire file, pointer at end
    f.seek(0)               # Reset pointer to the beginning
    first_line = f.readline()  # Now reads from the start again

Practical Pattern: Safe Read-or-Create

A common real-world pattern combines existence checking with file creation:

Python
import os

FILE_PATH = "config/settings.txt"

if os.path.exists(FILE_PATH):
    with open(FILE_PATH, "r") as f:
        settings = f.read()
        print("Loaded settings:", settings)
else:
    default_settings = "theme=dark\nlanguage=en\n"
    os.makedirs(os.path.dirname(FILE_PATH), exist_ok=True)
    with open(FILE_PATH, "w") as f:
        f.write(default_settings)
    print("Created default settings file.")

File Operation Cheat Sheet

Python
# Create and write (overwrites existing content)
with open("file.txt", "w") as f:
    f.write("content")

# Read entire file
with open("file.txt", "r") as f:
    data = f.read()

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

# Append to existing file
with open("file.txt", "a") as f:
    f.write("new content\n")

# Check if file exists
import os
os.path.exists("file.txt")  # → True or False

# Delete a file
os.remove("file.txt")

# Create directories if needed
os.makedirs("path/to/dir", exist_ok=True)

What's Next?

Now that you can persist data to files, the next logical step is learning how to handle things when they go wrong. In the next tutorial, we'll explore error handling in Python — using try, except, finally, and custom exceptions to write robust programs that fail gracefully instead of crashing.

Happy coding. 🐍