In this tutorial I'll briefly explain how one can load the content of an ASCII file into a single string.
The deferred class FILE cannot be used directly as it is deferred.
The descendant PLAIN_TEXT_FILE is what we're actually looking for.
Basic steps
The basic steps are the following:
local
l_file: PLAIN_TEXT_FILE
l_content: STRING
do
create l_file.make_create_read_write (a_path)
l_file.read_stream (l_file.count)
l_content := l_file.last_string.twin
l_file.close
end
Let's have a brief look at each single line:
Opening a file
We instantiate the class and open the file directly for read.
Doing it like that has the advantage that it is just one line.
create l_file.make_create_read_write (a_path)
Note: It might happen that the postcondition is violated if you don't have the permission to do the file operation for the path specified. We will later take this into account.
Reading from a file
l_file.read_stream (l_file.count)
There are several other read methods available. For example you can use read_line to read a single line of a given file.
l_content := l_file.last_string.twin
As the command/query separation principle is implemented, the result is always stored in last_....
Reading an integer from an ASCII file would look the following:\
l_file.read_integer
l_integer := l_file.last_integer
Note that we perform a call to twin for instances of type string as there is only one buffer per file object and it is overwritten by the next read command. If we do a twin we make sure that we don't have any surprising side effects later. So in general I recommend to do it for all non expanded types.
Closing a file
l_file.close
Last but not least we close the file to free system resources.
However, if we simply set the reference to Void this will do job as well, as the garbage collector calls dispose before he collects the object, which in turn calls close if the file is still open.
Better error handling
The creation feature we used so far initializes the object and tries to open the file right away. If the files does not exist an attempt to create it will be made. Which may possibly fail if you don't have the proper rights to do so. Still, the file will be empty and this is maybe not what the original intention of the programmer was.
To get more control over the whole process I recommend a different, more sophisticated way of achieving the same:
local
l_file: PLAIN_TEXT_FILE
do
create l_file.make (a_path)
-- We perform several checks until we make a real attempt to open the file.
if not l_file.exists then
print ("error: '" + a_path + "' does not exist%N")
else
if not l_file.is_readable then
print ("error: '" + a_path + "' is not readable.%N")
else
l_file.open_read
l_file.read_stream (l_file.count)
Result := l_file.last_string.twin
l_file.close
end
end
end
This feature checks several possible error conditions and prints error messages. The pattern will be the same in most cases, but the error handling will most likely depend on you application. So you could write a feature suited for your application with proper error handling and then simply reuse that feature whenever you need to open a file. If you don't want to copy paste the code all the time and just change the two error handling lines a more fine tuned class could implement the observer pattern implemented by using agents.