Python online course

How to Read and Write Files in Python

Learn how to create, open, read, write, append to, and safely close text files in Python using open(), file modes, with statements, and UTF-8 encoding.

Python includes built-in file input and output (I/O), so basic text-file operations do not require a third-party package. A file is a named collection of data stored on a filesystem. Python gives you a file object when you open a file. That object provides methods for reading, writing, and closing the file.

This lesson assumes that you can run a basic Python script, use variables and strings, call functions and methods, and work with indentation. For a review, see Python strings and how to run Python code.

Opening a file with open()

The built-in open() function opens a file and returns a file object. Its first argument is the filename, or a path identifying the file. Its second argument is the file mode, a string that controls whether Python reads, writes, appends, or both reads and writes.

file_object = open('notes.txt', 'r')

Use a descriptive variable name such as file_object, input_file, or output_file. In this example, notes.txt is opened for reading.

Relative and absolute paths

A relative filename such as 'notes.txt' is resolved relative to the program's current working directory. This is the directory Python uses as the starting point for relative paths. It may not be the same directory as the script file, depending on how the program was started.

An absolute path identifies a file from the root of the filesystem. For example, a Windows path might look like C:\\Users\\Sam\\notes.txt, while a Unix-like path might look like /home/sam/notes.txt. Relative paths are convenient for files belonging to a project; absolute paths are useful when you need to identify one exact location.

Opening behavior depends on both the selected mode and whether the file already exists. Read mode expects an existing file, while write and append modes can create a missing file.

Python text-file modes

Text mode is the default. You can write open('notes.txt', 'r') or simply open('notes.txt'); both use read mode. The most important beginner modes are:

Mode — Primary purpose — Must already exist — Creates if missing — Effect on existing content

r — Read an existing file — Yes — No — Preserves content

w — Write a new file — No — Yes — Replaces all existing content

a — Add content at the end — No — Yes — Preserves existing content and writes after it

r+ — Read and write — Yes — No — Preserves content initially, but writes can overwrite data at the current cursor position

Write mode is destructive: opening an existing file with 'w' clears its contents before writing. Use 'a' when the goal is to preserve earlier content and add new content.

Python also supports binary modes such as 'rb' and 'wb' for data such as images or compressed files. Binary operations use bytes rather than decoded text and are outside the basic text-file workflow.

Writing text to a file

The file object's write() method stores a string in the file. It returns the number of characters written, but you usually do not need to use that return value. write() does not automatically add a line break, so include the newline character \n when separate lines are needed.

Manual closing with write mode

fw = open('new_file.txt', 'w')
fw.write('Have a nice day!\n')
fw.write('You too!')
fw.close()

This creates new_file.txt in the current working directory if it does not exist. If the file already exists, mode 'w' removes its previous contents first. The first call writes a line ending; the second call writes text without adding another line ending.

You can write several lines in one string as well:

with open('greetings.txt', 'w', encoding='utf-8') as file:
    file.write('Hello!\nGoodbye!\n')

The explicit encoding='utf-8' tells Python how to represent characters in the file. UTF-8 is a common choice when text may contain accented letters, symbols, or characters from different writing systems.

Appending content

Open a file with mode 'a' to add new text after its existing content. Append mode creates the file if it is missing and does not clear data that is already there.

with open('new_file.txt', 'a', encoding='utf-8') as file:
    file.write('\nAnother line')

The leading \n starts the new text on a separate line. Whether you need it depends on how the existing file ends. A log-style example can add a record without deleting earlier records:

with open('activity.log', 'a', encoding='utf-8') as log_file:
    log_file.write('User signed in\n')

Use 'w' when replacing a file is intentional. Use 'a' when preserving and extending the file is the goal.

Reading a file

Read the entire file with read()

Use read() to retrieve all remaining text, or pass a number to retrieve up to that many characters.

with open('new_file.txt', 'r', encoding='utf-8') as file:
    content = file.read()

print(content)

This approach is simple and works well for small files. It stores the retrieved content in one string, so a very large file may require more memory than line-by-line processing.

Read one line with readline()

readline() returns the next line as a string. The returned line normally includes its trailing newline character.

with open('new_file.txt', encoding='utf-8') as file:
    first_line = file.readline()
    second_line = file.readline()

print(first_line, end='')
print(second_line, end='')

Call readline() repeatedly when you need precise control over one line at a time.

Read the remaining lines with readlines()

readlines() returns the remaining lines as a list of strings. Newline characters are usually included in those strings.

with open('new_file.txt', encoding='utf-8') as file:
    lines = file.readlines()

for line in lines:
    print(line, end='')

This is convenient when you need a list of all lines, but it loads the remaining lines into memory.

Iterate directly over the file

A file object can be used directly in a for loop. Python then supplies one line at a time, which is usually a good choice for large text files.

with open('new_file.txt', encoding='utf-8') as file:
    for line in file:
        clean_line = line.rstrip('\n')
        print(clean_line)

rstrip('\n') removes trailing newline characters for processing or display. Do not remove whitespace automatically if that whitespace is meaningful to your data.

Method — What it returns — Best use case — Important behavior

read() — One string — Reading all or a specified amount of text — Starts at the current cursor position

readline() — The next line as a string — Reading one line at a time with manual control — Usually includes the newline

readlines() — A list of remaining line strings — When a list of lines is useful — Loads those lines into memory

for line in file — One line per loop iteration — Memory-efficient processing — Naturally reaches the end of the file

The file cursor

Reading begins at the file's current cursor position. After read() consumes all content, a second read() normally returns an empty string because the cursor is at the end.

with open('new_file.txt', encoding='utf-8') as file:
    first_read = file.read()
    file.seek(0)
    second_read = file.read()

seek(0) moves the cursor back to the beginning. You can also store the first result or close and reopen the file.

Closing files and managing resources

Close a file after using it. Closing flushes buffered output so pending data is sent to the filesystem and releases operating-system resources associated with the open file.

With manual handling, call close() even if an operation appears to succeed:

file_object = open('notes.txt', 'r', encoding='utf-8')
content = file_object.read()
file_object.close()

The preferred modern pattern is the with statement. It creates a context manager for the file and closes the file automatically when the indented block ends, including when an error occurs inside the block.

with open('notes.txt', 'r', encoding='utf-8') as file:
    content = file.read()

print(content)

After the block, the file has been closed. Prefer this pattern for reading and writing because it is shorter and safer than relying on a later manual close() call.

Handling expected file errors

If you open a missing file in read mode, Python raises FileNotFoundError. Common causes include a misspelled filename, looking in the wrong current working directory, or trying to read a file that has not yet been created.

try:
    with open('input.txt', encoding='utf-8') as file:
        content = file.read()
except FileNotFoundError:
    print('Could not find input.txt. Check the filename and path.')
else:
    print(content)

Use a try/except block when a missing file is an expected possibility and you can provide a useful response. For more practice, see try and except statements and catching specific exceptions.

Common problems and fixes

  • Earlier contents disappeared: The file was opened with 'w', which truncates an existing file. Use 'a' to add content without deleting existing data.
  • FileNotFoundError in read mode: Check the filename, current working directory, and path. Create the file first if it is supposed to be generated by the program.
  • Text appears as one continuous line: Add \n where line breaks are required. write() does not insert them automatically.
  • Changes are not reliably visible: Ensure the file is closed, or use with open(...) so Python closes it automatically.
  • Accented characters display incorrectly: The file's encoding may differ from Python's assumed encoding. Specify encoding='utf-8' when that matches the file's encoding.
  • A second read returns no content: The cursor is probably at the end. Use seek(0), reopen the file, or reuse the result from the first read.

Summary

  • Use the built-in open() function to obtain a file object.
  • Choose the mode based on the operation: r for reading, w for replacement, a for appending, and r+ for reading and writing an existing file.
  • Use write() with explicit \n characters when writing separate lines.
  • Choose read(), readline(), readlines(), or direct iteration according to the amount and form of data you need.
  • Prefer with open(..., encoding='utf-8') so files close automatically and text encoding is explicit.
  • Remember that relative paths use the current working directory, and handle expected missing-file errors with FileNotFoundError.