Python · Fundamentals

File Handling in Python: The Complete Beginner's Guide

Everything you need to read, write, append, and manage files in Python, explained with working code, real output, and the reasoning behind every mode and method.

By Arj Updated July 2026 18 min read Python 3

1What is file handling

Almost every real program eventually needs to remember something after it closes: a saved game, a log of what happened, a config the user set last time, a report someone can open later. That is what file handling is for. It is how a running Python program talks to the file system underneath it rather than just holding values in memory that vanish the moment the script ends.

In Python, file handling covers five basic operations:

  • Creating a new file on disk
  • Reading content out of an existing file
  • Writing data into a file
  • Modifying existing content
  • Deleting a file entirely

Python handles all of this through a small set of built-in functions and a consistent, readable syntax, so once the core pattern clicks, the same approach carries you through logs, config files, CSV data, and even binary formats like images.

Where this fits in your learning path
If you have not yet worked through Python basics, start with the Python tutorial first. File handling assumes comfort with variables, loops, and functions, since almost every real example combines all three.

2Opening a file and file modes

Every file operation in Python starts with the built-in open() function. It takes a filename and a mode, and returns a file object you then read from or write to.

python
file = open("example.txt", "r")

The general syntax is straightforward: a filename (or path) first, then a mode string second.

syntax
open(filename, mode)

Common file modes

ModeDescription
"r"Read. This is the default if no mode is given, and it fails if the file does not exist.
"w"Write. Creates the file if it does not exist, and completely overwrites it if it does.
"a"Append. Creates the file if needed, and adds new content to the end without touching what is already there.
"x"Create. Makes a new file, but raises an error if the file already exists, useful as a safety check.
"b"Binary mode. Reads or writes raw bytes instead of decoded text.
"t"Text mode. This is the default, and decodes content as a string using your system's text encoding.

These modes combine, so you will often see something like "rb" (read binary) or "wt" (write text, though this is the same as plain "w" since text is already the default).

The mode you pick can destroy data
"w" mode wipes the file the instant it is opened, before you have written a single byte. If you open an existing file in "w" mode by mistake, its previous content is gone immediately, not after you call write(). Double-check the mode whenever a file you care about is involved.

3Reading a file three ways

Assume example.txt contains these two lines:

example.txtHello Welcome to Python

Method 1: read()

Reads the entire file into a single string, whitespace and newlines included. This is the simplest option for small files where you want everything at once.

python
file = open("example.txt", "r")
content = file.read()
print(content)
file.close()
OutputHello Welcome to Python

Method 2: readline()

Reads a single line at a time, moving forward through the file with each call. Useful when you want to process a file line by line without loading it all into memory.

python
file = open("example.txt", "r")
line1 = file.readline()
line2 = file.readline()
print(line1, line2)
file.close()

Method 3: readlines()

Reads every line and returns them as a list of strings, each one still carrying its trailing newline character. Looping over that list is the standard way to process a file line by line.

python
file = open("example.txt", "r")
lines = file.readlines()
for line in lines:
    print(line.strip())
file.close()
Why .strip() shows up so often
Every line from readlines() or readline() still has its newline character at the end. line.strip() removes leading and trailing whitespace, including that newline, which is why you will see it in almost every line-by-line reading example.
The best way to actually iterate a file
In practice, looping directly over the file object is more memory efficient than calling readlines() first, since it reads one line at a time instead of loading everything into a list upfront: for line in file: print(line.strip()). This matters once files get large.

4Writing and appending to a file

Overwrite a file

"w" mode replaces the entire contents of the file with whatever you write. If the file does not exist yet, it is created.

python
file = open("example.txt", "w")
file.write("This replaces everything.\n")
file.write("Second line.")
file.close()

Append to a file

"a" mode keeps the existing content and adds new text to the end, making it the right choice for anything like logging where you never want to lose earlier entries.

python
file = open("example.txt", "a")
file.write("\nThis is a new line.")
file.close()
write() does not add newlines for you
Unlike print(), write() never inserts a line break automatically. If you want each call to land on its own line, you have to include \n yourself, as in the examples above.

5The with statement, and why it matters

Every example so far ends with an explicit file.close() call, and every one of those calls is a place a bug can sneak in. If an error happens between open() and close(), the close line never runs, and the file stays open longer than it should, sometimes locking it or losing buffered writes that never actually made it to disk.

The with statement solves this by managing the file's lifecycle for you. It opens the file, gives you a variable to work with, and guarantees the file is closed when the block ends, whether it finished normally or an exception was raised partway through.

python
with open("example.txt", "r") as file:
    content = file.read()
    print(content)
# file is automatically closed here, even if an error occurred above

No need to call file.close() manually, and no risk of forgetting it. This is why with open(...) is the pattern used in essentially all professional Python code, and it is the version you should default to from here on.

Rule of thumb
If you ever see open() without a with statement in your own code, that is a sign to rewrite it. There is no real downside to with, and it removes an entire category of bugs.

6Reading and writing at the same time

Mode "r+" opens a file for both reading and writing without truncating it first, which makes it useful when you need to inspect and modify a file in the same session.

python
with open("example.txt", "r+") as file:
    file.write("Start: ")
    content = file.read()
    print(content)
The cursor position matters
A file object keeps an internal cursor tracking where the next read or write will happen. Writing first moves the cursor forward, so the following read() only picks up whatever comes after that point, not the whole file. If you need to read from the beginning after writing, call file.seek(0) first to reset the cursor.

7Working with binary files

Text mode assumes the file contains readable characters and decodes it using a text encoding. Binary files, images, PDFs, audio, compiled programs, are not text at all, and opening them in text mode either corrupts the data or raises a decoding error. Binary mode reads and writes raw bytes instead:

python
with open("image.jpg", "rb") as file:
    data = file.read()

data here is a bytes object rather than a string. You will see this pattern whenever a program downloads a file, copies an image, or hands raw content to another library, for example reading image data for computer vision often starts from exactly this kind of byte-level file access before OpenCV decodes it into pixel arrays.

8Checking if a file exists

Trying to read a file that is not there raises an error, so it is common to check first using the standard library's os module:

python
import os

if os.path.exists("example.txt"):
    print("File exists!")
else:
    print("File not found.")
Checking versus handling the error
os.path.exists() is useful, but it does not fully protect you: the file could be deleted by another process in the instant between your check and your open() call. In production code it is usually more robust to just attempt the open and catch the exception, which section 11 below covers.

9Deleting a file

The same os module handles deletion:

python
import os

os.remove("example.txt")
There is no undo
os.remove() deletes the file permanently and immediately, it does not go to a recycle bin or trash folder. Always be certain of the path before calling it, and consider checking os.path.exists() first if the file might already be gone, since removing a nonexistent file raises FileNotFoundError.

10Copying file content

You do not need any special library to copy a text file's contents, opening both files at once and passing data between them is enough:

python
with open("source.txt", "r") as src:
    with open("destination.txt", "w") as dest:
        dest.write(src.read())

You can nest with statements like this, or combine them on one line using a comma, both open at the same time and both close automatically when the block ends.

python
with open("source.txt", "r") as src, open("destination.txt", "w") as dest:
    dest.write(src.read())
For large files, copy in chunks
Reading an entire large file into memory with src.read() can be wasteful. For big files, read and write in fixed-size chunks in a loop instead, or use the standard library's shutil.copyfile(), which is built for exactly this and handles the chunking for you.

11Common errors and how to fix them

ErrorReasonFix
FileNotFoundErrorThe file does not exist at the given pathCheck the filename and path, or catch the error with try/except
PermissionErrorYour program does not have write or read accessCheck file and folder permissions, or run with the correct account
IOError / OSErrorA general problem reading or writing, such as a full disk or a removed driveWrap the operation in try/except and handle it gracefully
UnicodeDecodeErrorOpened a binary file in text mode, or the file uses a different encoding than expectedOpen with "rb" for binary data, or pass the correct encoding= argument
ValueError: I/O operation on closed fileTried to read or write after the file was already closedKeep the operation inside the with block, do not use the file object afterward

Handling these gracefully means wrapping the risky operation in a try block rather than letting the whole program crash:

python
try:
    with open("data.txt", "r") as f:
        print(f.read())
except FileNotFoundError:
    print("File not found.")
Catch specific exceptions, not everything
Catching FileNotFoundError specifically, rather than a bare except:, means genuine bugs elsewhere in your code still surface as errors instead of being silently swallowed. Only catch what you actually expect and know how to handle.

12Working with paths safely

Every example above uses a bare filename like "example.txt", which Python resolves relative to wherever the script was run from, not necessarily where the script file itself lives. This is one of the most common sources of "file not found" confusion for beginners, since running the same script from a different folder changes what a relative path points to.

For anything beyond a quick script, the standard library's pathlib module is the modern, recommended way to build paths that work correctly across Windows, macOS, and Linux without manually worrying about slash direction:

python
from pathlib import Path

# Build a path relative to this script's own location
script_dir = Path(__file__).parent
file_path = script_dir / "data" / "example.txt"

with open(file_path, "r") as f:
    print(f.read())
Why this matters across systems
Windows uses backslashes in paths while macOS and Linux use forward slashes. Building paths with pathlib's / operator, as shown above, produces the correct separator automatically for whichever system the code runs on, so you never have to hardcode one or the other.

13Best practices checklist

  • Always use with open() instead of manual open() and close() calls, so files are never left open by accident.
  • Handle errors with try/except around any file operation that touches user-provided paths or external data.
  • Prefer pathlib over string concatenation when building file paths, especially in code that might run on more than one operating system.
  • Be precise with modes. Reach for "a" instead of "w" whenever you do not want to risk erasing existing content.
  • Avoid hardcoding filenames where possible, pass them as function arguments or configuration values instead, so the same code can be reused for different files.
  • Read large files in chunks rather than loading them entirely into memory with a single read() call.

14What to learn next

Python's file handling is one of its most beginner-friendly features. With just a few lines of code you can read, write, and manage files, whether that is logs, datasets, or reports, and the with pattern you learned here carries forward into almost every other resource-handling situation in Python, including database connections and network sockets.

Once you are comfortable with basic file I/O, a natural next step is structured data formats built on top of these same principles:

  • CSV and JSON files, the two most common formats for structured data exchange, both readable with a few extra lines on top of what you learned here.
  • Reading large files efficiently, including chunked reading and generators that avoid loading an entire file into memory at once.
  • Handling directories and file trees with os and pathlib, walking folders, filtering by extension, and organizing output automatically.

The data storage in Python guide picks up exactly here and goes further into structured formats and persistence patterns. If you want the full sequence from fundamentals onward, the Python roadmap lays out what to study before and after this topic.

Keep building your Python skill tree
See exactly what to learn after file handling, in order, with every guide on this site mapped to a stage.
View the Python roadmap
Scroll to Top