Reading and Writing files
Perhaps one of the first programming terms I ever encountered was ‘input/output error’. That was back in the era of the Sinclair ZX81 when saving or loading your work was a matter of piping squawking noises into or out of a portable tape recorder. As you might expect, I/O errors were quite a common occurrence, requiring many nailbiting retries accompanied by adjustments of the volume and treble controls. Nostalgia aside, the early lesson I learned (after having to type in an entire BASIC adventure game by hand for the second time) was that storage and retrieval are a pretty fundamental feature. Thankfully, these days we take a hard drive pretty much for granted. So, let’s do some saving and loading.
PHP Code Example:
While I’m in a nostalgic mood, I’m going to work with a favourite poem: Jabberwocky by Lewis Carroll. Let’s write some lines to a file, append another line, and then read the result back in to memory.
// write
$fp = fopen("outfiles/php-jabberwocky.txt", "w");
fputs($fp, "'Twas brillig, and the slithy toves\n");
fputs($fp, "Did gyre and gimble in the wabe:\n");
fputs($fp, "All mimsy were the borogoves,\n");
fclose($fp);
// append
$fp = fopen("outfiles/php-jabberwocky.txt", "a");
fputs($fp, "And the mome raths outgrabe.\n");
fclose($fp);
// read
$fp = fopen("outfiles/php-jabberwocky.txt", "r");
while (($line = fgets($fp)) !== false) {
print $line;
}
fclose($fp);
So for each of these operations we use PHP’s fopen()
function with the second string argument w
for write, a
for append, r
for read. fopen()
returns a file resource. For write and append operations we use fputs()
with the file handle. For the final read, we use fgets()
.
Python Code Example:
Let’s repeat the process in Python. As you’ll see, it’s very similar.
# write
fobj = open("outfiles/python-jabberwocky.txt", "w")
fobj.write("'Twas brillig, and the slithy toves\n")
fobj.write("Did gyre and gimble in the wabe:\n")
fobj.write("All mimsy were the borogoves,\n")
fobj.close()
# append
fobj = open("outfiles/python-jabberwocky.txt", "a")
fobj.write("And the mome raths outgrabe.\n")
fobj.close()
# read
fobj = open("outfiles/python-jabberwocky.txt", "r")
for line in fobj:
print(line, end='')
fobj.close()
Because Python’s open()
returns an object, we can use methods on that for writing and reading. Other than that, the process is remarkably consistent across the languages.
Output
The output for both the PHP and Python examples should be the same:
'Twas brillig, and the slithy toves
Did gyre and gimble in the wabe:
All mimsy were the borogoves,
And the mome raths outgrabe.
Discussion
PHP’s venerable fopen()
and Python’s open()
are obviously quite similar. In both cases the functions require a file path and a mode argument to specify the way that the file will be processed. In these examples we have specified:
Argument | Description |
---|---|
w | write (truncating if data present) |
a | append |
r | read |
Additional modes in Python are:
Argument | Description |
---|---|
x | open for creation (errors if file exists) |
b | (modifier) binary mode. Will read or write raw bytes. |
t | (modifier) text mode (default/implicit). Will use specified or default encoding. |
+ | (modifier) open for both reading and writing |
The modifier modes can be used in conjunction with others – so wb
opens a file for writing in binary mode (writing raw bytes) and w+
opens a file for writing and reading. wt
and rt
are identical to r
and w
respectively since text mode is the default.
PHP’s fgets()
method will return a string unless the end of the file (EOF) has been reached, in which case it returns false
. The nearest equivalent to this in Python is the file object’s readline()
method which will return an empty string at EOF. Here’s a slightly more clunky version of the line iteration example to illustrate the use ofreadline()
.
fobj = open("outfiles/python-jabberwocky.txt", "r")
while (line := fobj.readline()) != '':
print(line, end='')
fobj.close()
Even though Python 3.8 gave us the so-called walrus operator (:=
) which allows us to assign the results of a loop test to a variable, overall this syntax is far from pretty. Luckily, as we have seen, Python’s file objects are iterable – which means we can use them directly in a for
loop without needing to explicitly call readline()
at all.
Another PHP trick for reading files is file()
which reads the contents of a file into an array.
$lines = file("outfiles/php-jabberwocky.txt");
foreach ($lines as $line) {
print $line;
}
Python’s file object provides the readlines()
which reads the contents of a file into a list.
fobj = open("outfiles/python-jabberwocky.txt", "r")
lines = fobj.readlines()
for line in lines:
print(line, end='')
fobj.close()
Error checking
Notice that we have been careful to call close on the file object after reading or writing in the examples. It is regarded as bad practice leave a file unclosed in your program. One way, besides just leaving it out, you might fail to call close()
is after an error stops execution. If open()
fails it will raise an OSError. You can anticipate this problem using try
and finally
fobj = open("outfiles/python-jabberwocky.txt", "r")
try:
for line in fobj:
print(line, end='')
finally:
fobj.close()
If something goes wrong with the file processing in this example, the finally
clause will be invoked, allowing us to clean things up.
A more compact way of doing the same thing is a with
clause. The with
statement is designed to work with context manager objects (that is objects that implement the special methods __enter__()
and __exit__()
). The object returned by open()
(io.TextIOBase) is a context manager and, in conjunction with the with
statement, it will implicitly close the file if an exception is encountered:
with open("outfiles/python-jabberwocky.txt", "r") as fobj:
for line in fobj:
print(line, end='')
NOTE We will cover error handling in much more detail in a future article
Further Reading:
Stay up to date with the Python for PHP Programmers project
Photo by Namroud Gorguis on Unsplash