Skip to main content

Files and Exceptions

Learning to work with files and save data will make your programs more relevant and useful. Users will be able to choose what data to enter and when to enter it, and they can use your programs and quit whenever they want, picking up where they left off later.

Reading from a File​

Reading an Entire File​

To work with the contents of a file, you first need to read the file into memory. The open() function takes one argument: the name of the file you want to open.

with open('pi_digits.txt') as file_object:
contents = file_object.read()
print(contents)

The keyword with closes the file once access to it is no longer needed. You could open and close the file by calling open() and close(), but if a bug in your program prevents the close() statement from executing, the file may never close.

File Paths​

When you pass a simple filename like 'pi_digits.txt' to the open() function, Python looks in the directory where the program being executed is stored.

Relative and Absolute File Paths​

# Relative path
with open('text_files/filename.txt') as file_object:

# Absolute path (Linux/macOS)
file_path = '/home/ehmatthes/other_files/text_files/filename.txt'
with open(file_path) as file_object:

# Absolute path (Windows)
file_path = 'C:\Users\ehmatthes\other_files\text_files\filename.txt'
with open(file_path) as file_object:
Windows Paths

Windows systems use a backslash (\) instead of a forward slash (/) when displaying file paths, but you can use forward slashes in your code even on Windows systems.

Reading Line by Line​

filename = 'pi_digits.txt'

with open(filename) as file_object:
for line in file_object:
print(line)

This might produce extra blank lines because each line in the text file ends with a newline character, and the print() function adds its own newline. You can remove these extra blank lines:

filename = 'pi_digits.txt'

with open(filename) as file_object:
for line in file_object:
print(line.rstrip())

Making a List of Lines from a File​

filename = 'pi_digits.txt'

with open(filename) as file_object:
lines = file_object.readlines()

for line in lines:
print(line.rstrip())

The readlines() method takes each line from the file and stores it in a list, which is returned to you.

Working with a File's Contents​

filename = 'pi_digits.txt'

with open(filename) as file_object:
lines = file_object.readlines()

pi_string = ''
for line in lines:
pi_string += line.strip()

print(pi_string)
print(len(pi_string))

Output:

3.1415926535897932384626433832795
32

Large Files: One Million Digits​

When Python reads from a text file, it interprets all text in the file as a string. Here's how you might work with a very large file:

filename = 'pi_million_digits.txt'

with open(filename) as file_object:
lines = file_object.readlines()

pi_string = ''
for line in lines:
pi_string += line.strip()

print(f"{pi_string[:52]}...")
print(len(pi_string))

Is Your Birthday Contained in Pi?​

filename = 'pi_million_digits.txt'

with open(filename) as file_object:
lines = file_object.readlines()

pi_string = ''
for line in lines:
pi_string += line.strip()

birthday = input("Enter your birthday, in the form mmddyy: ")
if birthday in pi_string:
print("Your birthday appears in the first million digits of pi!")
else:
print("Your birthday does not appear in the first million digits of pi.")

Writing to a File​

Writing to an Empty File​

filename = 'programming.txt'

with open(filename, 'w') as file_object:
file_object.write("I love programming.")

The second argument, 'w', tells Python that you want to open the file in write mode. You can open a file in:

  • read mode ('r')
  • write mode ('w')
  • append mode ('a')
  • read and write mode ('r+')
Write Mode

Opening a file in write mode ('w') will erase the contents of the file before returning the file object.

Writing Multiple Lines​

filename = 'programming.txt'

with open(filename, 'w') as file_object:
file_object.write("I love programming.\n")
file_object.write("I love creating new games.\n")

Appending to a File​

filename = 'programming.txt'

with open(filename, 'a') as file_object:
file_object.write("I also love finding meaning in large datasets.\n")
file_object.write("I love creating apps that can run in a browser.\n")

Exceptions​

Python uses special objects called exceptions to manage errors that arise during a program's execution. Whenever an error occurs that makes Python unsure what to do next, it creates an exception object.

Handling the ZeroDivisionError Exception​

print(5/0)

This will produce:

ZeroDivisionError: division by zero

Using try-except Blocks​

try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")

Output:

You can't divide by zero!

Using Exceptions to Prevent Crashes​

print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")

while True:
first_number = input("\nFirst number: ")
if first_number == 'q':
break
second_number = input("Second number: ")
if second_number == 'q':
break
try:
answer = int(first_number) / int(second_number)
except ZeroDivisionError:
print("You can't divide by zero!")
else:
print(answer)

The else Block​

try:
answer = int(first_number) / int(second_number)
except ZeroDivisionError:
print("You can't divide by zero!")
else:
print(answer)

The else block will run only if the try block was successful.

Handling the FileNotFoundError Exception​

filename = 'alice.txt'

try:
with open(filename, encoding='utf-8') as f:
contents = f.read()
except FileNotFoundError:
print(f"Sorry, the file {filename} does not exist.")

Analyzing Text​

filename = 'alice.txt'

try:
with open(filename, encoding='utf-8') as f:
contents = f.read()
except FileNotFoundError:
print(f"Sorry, the file {filename} does not exist.")
else:
# Count the approximate number of words in the file.
words = contents.split()
num_words = len(words)
print(f"The file {filename} has about {num_words} words.")

The split() method creates a list of words from a string.

Working with Multiple Files​

def count_words(filename):
"""Count the approximate number of words in a file."""
try:
with open(filename, encoding='utf-8') as f:
contents = f.read()
except FileNotFoundError:
print(f"Sorry, the file {filename} does not exist.")
else:
words = contents.split()
num_words = len(words)
print(f"The file {filename} has about {num_words} words.")

filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_women.txt']
for filename in filenames:
count_words(filename)

Failing Silently​

Sometimes you want your program to fail silently when an exception occurs and continue as if nothing happened. You can use the pass statement:

def count_words(filename):
"""Count the approximate number of words in a file."""
try:
with open(filename, encoding='utf-8') as f:
contents = f.read()
except FileNotFoundError:
pass
else:
words = contents.split()
num_words = len(words)
print(f"The file {filename} has about {num_words} words.")

Deciding Which Errors to Report​

How do you know when to report an error to your users and when to fail silently? If users know which files are supposed to be analyzed, they might appreciate a message informing them why some files were not analyzed. If users expect to see some results but don't know which files are supposed to be processed, they might not need to know that some files were unavailable.

Storing Data​

Many of your programs will ask users to input certain kinds of information. The json module allows you to dump simple Python data structures into a file and load the data from that file the next time the program runs.

Using json.dump() and json.load()​

import json

numbers = [2, 3, 5, 7, 11, 13]

filename = 'numbers.json'
with open(filename, 'w') as f:
json.dump(numbers, f)

Now let's write a program that loads the list back into memory:

import json

filename = 'numbers.json'
with open(filename) as f:
numbers = json.load(f)

print(numbers)

Output:

[2, 3, 5, 7, 11, 13]

Saving and Reading User-Generated Data​

import json

username = input("What is your name? ")

filename = 'username.json'
with open(filename, 'w') as f:
json.dump(username, f)
print(f"We'll remember you when you come back, {username}!")

Now let's write a program that greets a user whose name has already been stored:

import json

filename = 'username.json'

with open(filename) as f:
username = json.load(f)
print(f"Welcome back, {username}!")

Combining the Programs​

import json

filename = 'username.json'

try:
with open(filename) as f:
username = json.load(f)
except FileNotFoundError:
username = input("What is your name? ")
with open(filename, 'w') as f:
json.dump(username, f)
print(f"We'll remember you when you come back, {username}!")
else:
print(f"Welcome back, {username}!")

Refactoring​

Often, you'll come to a point where your code will work, but you'll recognize that you could improve the code by breaking it up into a series of functions:

import json

def get_stored_username():
"""Get stored username if available."""
filename = 'username.json'
try:
with open(filename) as f:
username = json.load(f)
except FileNotFoundError:
return None
else:
return username

def get_new_username():
"""Prompt for a new username."""
username = input("What is your name? ")
filename = 'username.json'
with open(filename, 'w') as f:
json.dump(username, f)
return username

def greet_user():
"""Greet the user by name."""
username = get_stored_username()
if username:
print(f"Welcome back, {username}!")
else:
username = get_new_username()
print(f"We'll remember you when you come back, {username}!")

greet_user()

Each function in this final version has a single, clear purpose. This compartmentalized work makes your code easier to write, read, test, and maintain.

Summary​

In this chapter you learned how to:

  • Read entire files and read files line by line
  • Work with absolute and relative file paths
  • Write text to files using write mode and append mode
  • Use try-except blocks to handle exceptions gracefully
  • Store Python data structures using the JSON format
  • Refactor code to make it cleaner and easier to extend

Files and exception handling will help you work with data from files and handle errors that might occur in your programs, making your programs more robust and user-friendly.