跳到主要内容

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.