12.2 Writing To and Reading From Files

Now, that we have learned how to load programs from different files, we want to look at writing results to files and reading in input from files in this section.

Before we can do any reading of or writing to the file, we have to open it and associate a stream with it. You can think of streams as connections to files. Streams have names that look like this, for instance: '$stream'(183368). You need these names, when specifying which stream to write to or read from. Luckily, you never really have to worry about the exact names of streams. Prolog assigns them these names and you usually just bind them to a variable and then pass this variable around. We'll see an example soon.

The inbuilt predicate open/3 opens a file and connects it to a stream.

open(+FileName,+Mode,-Stream)

The first argument of open is the name of the file, and in the last argument, Prolog returns the name that it assigns to the stream. Mode is one of read, write, append. read means that the file is opened for reading, and write and append both open the file for writing. In both cases, the file is created, if it doesn't exist, yet. If it does exist, however, write will cause the file to be overwritten, while append appends everything at the end of the file.

When you are finished with the file, you should close it again. That is done with the following predicate, where Stream is the name of a Stream as assigned by Prolog.

close(Stream)

So, programs that are writing to or reading from files will typically have the following structure:

open(myfile,write,Stream),
...  
   
do something  
                ...
close(Stream),

The predicates for actually writing things to a stream are almost the same as the ones we saw in Chapter 9 for writing to the screen. We have write, tab, and nl. The only thing that's different is that we always give the stream that we want to write to as the first argument.

Here is a piece of code that opens a file for writing, writes something to it, and closes it again.

?- open(hogwarts,write,OS),
   tab(OS,7),write(OS,gryffindor),nl(OS),
   write(OS,hufflepuff),tab(OS,5),write(OS,ravenclaw),nl(OS),
   tab(OS,7),write(OS,slytherin),
   close(OS).

The file hogwarts should afterwards look like this:

       gryffindor
hufflepuff     ravenclaw
       slytherin

Finally, there is a two-place predicate for reading in terms from a stream. read always looks for the next term on the stream and reads it in.

read(+Stream,+Term)

The inbuilt predicate at_end_of_stream checks whether the end of a stream has been reached. at_end_of_stream(Stream) will evaluate to true, when the end of the stream Stream is reached, i.e. when all terms in the corresponding file have been read.

Note, that read only reads in Prolog terms. If you want to read in arbitrary input, things become a bit more ugly. You have to read it character by character. The predicate that you need is get0(+Stream,-Char). It reads the next character from the stream +Stream. Char is the integer code of the character. That means that get0 returns 97, if the next character is a, for instance.

Usually, we are not interested in these integer codes, but in the characters or rather the atoms that are made up of a list of characters. Well, you can use the predicate atom_chars/2 to convert a list of integers into the corresponding atom. The first argument of atom_chars/2 is the atom and the second the list of integers. For example:

?- atom_chars(W,[113,117,105,100,100,105,116,99,104]).
W = quidditch

Here is the code for reading in a word from a stream. It reads in a character and then checks whether this character is a blank, a carriage return or the end of the stream. In any of these cases a complete word has been read, otherwise the next character is read.

readWord(InStream,W) :-
        get0(InStream,Char),
        checkCharAndReadRest(Char,Chars,InStream),
        atom_chars(W,Chars).
 
checkCharAndReadRest(10,[],_) :- !.  % Return
checkCharAndReadRest(32,[],_) :- !.  % Space
checkCharAndReadRest(-1,[],_) :- !.  % End of Stream
checkCharAndReadRest(end_of_file,[],_) :- !.
checkCharAndReadRest(Char,[Char|Chars],InStream) :-
        get0(InStream,NextChar),
        checkCharAndReadRest(NextChar,Chars,InStream).


Patrick Blackburn, Johan Bos and Kristina Striegnitz
Version 1.2.5 (20030212)