Once you’ve opened up a file, you’ll want to read or write to the file. First off, let’s cover reading a file. There are multiple methods that can be called on a file object to help you out.
At some point in your Python coding journey, you learn that you should use a context manager to open files. Python context managers make it easy to close your files once you’re done with them:
with open("dog_breeds.txt", "r") as reader:
print(reader.read())
results in
Pug
Jack Russell Terrier
English Springer Spaniel
German Shepherd
Staffordshire Bull Terrier
Cavalier King Charles Spaniel
Golden Retriever
West Highland White Terrier
Boxer
Border Terrier
Here’s an example of how to read 5 bytes of a line each time using the Python .readline() method:
with open('dog_breeds.txt', 'r') as reader:
>>> # Read & print the first 5 characters of the line 5 times
>>> print(reader.readline(5))
>>> # Notice that line is greater than the 5 chars and continues
>>> # down the line, reading 5 chars each time until the end of the
>>> # line and then "wraps" around
>>> print(reader.readline(5))
>>> print(reader.readline(5))
>>> print(reader.readline(5))
>>> print(reader.readline(5))
results in:
Pug
Jack
Russe
ll Te
rrier
Here’s an example of how to read the entire file as a list using the Python .readlines() method, which is likely the most useful method- for example we can get a list of strings that we could process into a dict- we can see this later
f = open('dog_breeds.txt')
f.readlines() # Returns a list object
results in a list of str:
['Pug\n', 'Jack Russell Terrier\n', 'English Springer Spaniel\n', 'German Shepherd\n', 'Staffordshire Bull Terrier\n', 'Cavalier King Charles Spaniel\n', 'Golden Retriever\n', 'West Highland White Terrier\n', 'Boxer\n', 'Border Terrier\n']
Once we have this file data imported, we can do operations on it, creating information from the data such as averages, counts, graphs, sub-sets of data and so on.
The with statement initiates a context manager. In this example, the context manager opens the file and manages the file resource as long as the context is active. In general, all the code in the indented block depends on the file object being open. Once the indented block either ends or raises an exception, then the file will close.
If you’re not using a context manager, then you might explicitly close files with the try … finally approach:
try:
file = open("hello.txt", mode="w")
file.write("Hello, World!")
except:
print("File could not be opened...")
finally:
file.close()
The finally block that closes the file runs unconditionally, whether the try block succeeds or fails. While this syntax effectively closes the file, the Python context manager offers less verbose and more intuitive syntax. Additionally, it’s a bit more flexible than simply wrapping your code with try … except ... finally.
A context manager in Python is an object that manages resources, ensuring they are properly set up and cleaned up. It implements two special methods:
__enter__(): Called when entering the context (the with block). It sets up the resource.
__exit__(): Called when exiting the context. It cleans up the resource, even if an error occurs.
The most common use of context managers is with file operations. The with statement simplifies file handling by automatically closing the file after its block is executed. Here’s how it works:
with open('example.txt', 'r') as file:
content = file.read()
# The file is automatically closed here
Automatic Resource Management: Files are closed automatically, preventing resource leaks.
Error Handling: If an error occurs within the with block, the file is still closed properly.
Cleaner Code: Reduces the need for explicit close calls, making the code easier to read and maintain.
You can create custom context managers using classes or decorators. Here’s an example of a class-based context manager:
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_value, traceback):
self.file.close()
with FileManager('example.txt', 'w') as f:
f.write('Hello, World!')
This custom context manager opens a file and ensures it is closed after use, similar to the built-in open() function.
Now let’s dive into writing files. As with reading files, file objects have multiple methods that are useful for writing to a file.
We do not explicitly close the file, but it IS closed after we de-dent!
with open('dog_breeds.txt', 'r') as reader:
# Note: readlines doesn't trim the line endings
dog_breeds = reader.readlines()
with open('dog_breeds_reversed.txt', 'w') as writer:
# Alternatively you could use
# writer.writelines(reversed(dog_breeds))
# Write the dog breeds to the file in reversed order
for breed in reversed(dog_breeds):
writer.write(breed)
Once we de-dent, the file handler is closed, but we can still work with any data that was read and stored by our program.
Sometimes, you may want to append to a file or start writing at the end of an already populated file. This is easily done by using the 'a' character for the mode argument:
with open('dog_breeds.txt', 'a') as a_writer:
a_writer.write('\nBeagle')
When you examine dog_breeds.txt again, you’ll see that the beginning of the file is unchanged and Beagle is now added to the end of the file:
with open('dog_breeds.txt', 'r') as reader:
print(reader.read())
will result in:
Pug
Jack Russell Terrier
English Springer Spaniel
German Shepherd
Staffordshire Bull Terrier
Cavalier King Charles Spaniel
Golden Retriever
West Highland White Terrier
Boxer
Border Terrier
Beagle
You can imagine that this might be useful for a "high_scores" file for a game, or logging activity, or any number of other uses.
It is important that when opening files for read/write operations, that the file is closed afterwards- using the context manager takes care of this for you.
The close() function closes the file and frees the memnory space acquired by that file. It is used at the time when the file is no longer needed or if it is to be opened in a different file mode.
# Opening and Closing a file "MyFile.txt"
# for object named file1.
file1 = open("MyFile.txt","a")
file1.close()
It's fairly ineffecient to open the file in a or w and then reopening it in r to read any lines. Luckily we can access the file in the following modes:
r+ : Reading and writing. Cannot truncate the file.
w+ : Writing and reading. Truncates the file.
a+ : Appending and Reading. Creates a new file, if none exists.
Most of the file methods we've looked at work in a certain location in the file. .write() writes at a certain location in the file. .read() reads at a certain location in the file and so on. You can think of this as moving your pointer around in the notepad to make changes at specific location.
Opening the file in w is akin to opening the .txt file, moving your cursor to the beginning of the text file, writing new text and deleting everything that follows. Whereas opening the file in a is similiar to opening the .txt file, moving your cursor to the very end and then adding the new pieces of text.
It is often very useful to know where the 'cursor' is in a file and be able to control it. The following methods allow us to do precisely this -
.tell() - returns the current position in bytes
.seek(offset,from) - changes the position by 'offset' bytes with respect to 'from'. From can take the value of 0,1,2 corresponding to beginning, relative to current position and end
Lets make a simple version of our dogs file - dogs.txt - to fold only the following:
Pug
Jack Russell
Springer Spaniel
German Shepherd
Now lets open it and see what we find...
with open('/dogs.txt', 'a+') as testwritefile:
print("Initial Location: {}".format(testwritefile.tell()))
data = testwritefile.read()
if (not data): #empty strings return false in python
print('Read nothing')
else:
print(testwritefile.read())
testwritefile.seek(0,0) # move 0 bytes from beginning.
print("\nNew Location : {}".format(testwritefile.tell()))
data = testwritefile.read()
if (not data):
print('Read nothing')
else:
print(data)
print("Location after read: {}".format(testwritefile.tell()) )
results in the following output:
Initial Location: 49
Read nothing
New Location : 0
Pug
Jack Russell
Springer Spaniel
German Shepherd
Location after read: 49
Steps executed:
When we opened the file, it was pointing to the end of the file, and so read nothing.
We moved the pointer to the beginning of the file
Next we read and print the data in the file
Then we print the position of the pointer after reading and printing all the data
Finally, De-denting closes the file