12.3 Reading from Files
In this section we show how to read from files. Reading information from files is straightforward in Prolog — or at least, it is if this information is given in the form of Prolog terms followed by full stops. Consider the file houses.txt :
gryffindor. hufflepuff. ravenclaw. slytherin.
Here is a Prolog program that opens this file, reads the information from it, and displays it on the screen:
main:- open('houses.txt',read,Str), read(Str,House1), read(Str,House2), read(Str,House3), read(Str,House4), close(Str), write([House1,House2,House3,House4]), nl.
This opens a file in reading mode, then reads four Prolog terms using the built-in predicate read/2 , closes the stream, and prints the information as a list.
All very straightforward. Nonetheless, the read/2 predicate needs to be handled with care. First of all, it only is able to handle Prolog terms (we’ll say more about this problem shortly). And secondly, it will cause a run-time error if we use it to read from a stream when there is nothing to read. Is there an elegant way to overcome this second problem?
There is. The built-in predicate at_end_of_stream/1 checks whether the end of a stream has been reached, and can be used as a safety-net. For a stream X , at_end_of_stream(X) will evaluate to true when the end of the stream X is reached (in other words, when all terms in the corresponding file have been read).
The following code is a modified version of our earlier reading-in program, which shows how at_end_of_stream/1 can be incorporated:
main:- open('houses.txt',read,Str), read_houses(Str,Houses), close(Str), write(Houses), nl. read_houses(Stream,):- at_end_of_stream(Stream). read_houses(Stream,[X|L]):- \+ at_end_of_stream(Stream), read(Stream,X), read_houses(Stream,L).
Now for the nastier problem. Recall that read/2 only reads in Prolog terms. If you want to read in arbitrary input, things become rather unpleasant, as Prolog forces you to read information on the level of characters. The predicate that you need in this case is get_code/2 which reads the next available character from a stream. Characters are represented in Prolog by their integer codes. For example, get_code/2 will return 97 if the next character on the stream is an a .
Usually we are not interested in these integer codes, but in the characters — or rather, in the atoms that are made up of lists of these characters. How do we get our hands on these (lists of) characters? One way is to use the built-in predicate atom_codes/2 that we introduced in Chapter 9 to convert a list of integers into the corresponding atom. We’ll use this technique in the following example, a predicate that reads in a word from a stream.
readWord(InStream,W):- get_code(InStream,Char), checkCharAndReadRest(Char,Chars,InStream), atom_codes(W,Chars). checkCharAndReadRest(10,,_):- !. checkCharAndReadRest(32,,_):- !. checkCharAndReadRest(-1,,_):- !. checkCharAndReadRest(end_of_file,,_):- !. checkCharAndReadRest(Char,[Char|Chars],InStream):- get_code(InStream,NextChar), checkCharAndReadRest(NextChar,Chars,InStream).
How does this work? It reads in a character and then checks whether this character is a blank (integer code 32), a new line (10) or the end of the stream ( − 1). In any of these cases a complete word has been read, otherwise the next character is read.