Previous Up Next

11.2  Communication via Streams

The contents of a stream may be interpreted in one of three basic ways. The first one is to consider it as a sequence of characters, so that the basic unit to be read or written is a character. The second one interprets the stream as a sequence of tokens, thus providing an interface to the Prolog lexical analyzer and the third one is to consider a stream as a sequence of Prolog terms.

11.2.1  Character I/O

The get/1,2 and put/1,2 predicates corresponds to the first way of looking at streams. The call

get(Char)

takes the next character from the current input stream and matches it as a single character with Char. Note that a character in ECLiPSe is represented as an integer corresponding to the ISO-8859-1 (iso_latin_1) code of the character. If the end of file has been reached then an exception is raised and -1 is returned. The call

put(Char)

puts the char Char on to the current output stream. The predicates

get(Stream, Char)

and

put(Stream, Char)

work similarly on the specified stream.

The input and output is normally buffered by ECLiPSe. To make I/O in raw mode, without buffering, the predicates tyi/1,2 and tyo/1,2 are provided.

11.2.2  Token I/O

The predicates read_token/2 and read_token/3:

read_token(TokenClass)
read_token(StreamTokenClass)

represent the second way of interpreting stream contents. They read the next token from the current input stream, unify it with Token, and its token class is unified with Class. A token is either a sequence of characters with the same or compatible character class, e.g., ab_1A, then it is a Prolog constant or variable, or a single character, e.g., ’)’. The token class represents the type of the token and its special meaning, e.g., fullstop, comma, open_par, etc. The exact definition of character classes and tokens can be found in appendices A.2.1 and A.2.3, respectively.

A further, very flexible possibility to read a sequence of characters is provided by the built-in read_string/5

read_string(StreamSepCharsPadCharsSeparatorString)

Here, the input is read up to a specified delimiter, and returned as an ECLiPSe string.

In particular, one line of input can be read as follows:

read_line(Stream, String) :-
    read_string(Stream, end_of_line, "", _Separator, String).

The SepChar argument allows the specification of padding characters, which will be ignored before and after separators. Once a string has been read, string manipulation predicates like split_string/4 can be used to break it up into even smaller components.

11.2.3  Term I/O

The read/1,2 and write/1,2 predicates correspond to the third way of looking at streams. For input, the goal

read(Term)

reads the next ECLiPSe term from the current input stream and unifies it with Term. The input term must be followed by a full stop, that is, a ’.’ character followed by a layout character (tab, space or newline) or by the end of file. The exact definition of the term syntax can be found in appendix A.

If end of file has been reached then an exception is raised, the default handler causes the atom end_of_file to be returned. A term may be read from a stream other than the current input stream by the call

read(Stream, Term)

which reads the term from the named stream.

For additional information about other options for reading terms, in particular for how to get variable names, refer to readvar/3, read_term/2 and read_term/3. For reading and processing complete ECLiPSe source code files, use the library(source_processor).

For output, the goal

write(Term)

writes Term to the current output stream. This is done by taking the current operator declarations into account. Output produced by the write/1,2 predicate is not (necessarily) in a form suitable for subsequent input to a Prolog program using the read/1 predicate, for this purpose writeq/1,2 is to be used. The goal

write(Stream, Term)

writes Term to the named output stream. For more details about how to output terms in different formats, see section 11.4.

When the flag variable_names is switched off, the output predicates are not able to write free variables in their source form, i.e., with the correct variable names. Then the variables are output in the form

_N

where N is a number which identifies the variable (but note that these numbers may change on garbage collection and can therefore not be used to identify the variable in a more permanent way). Occasionally the number will be prefixed with the lower-case letter l, indicating that the variable is in a short-lived memory area called the local stack (see 20).

11.2.4  Newlines

Newlines should be output using either nl/0, nl/1, writeln/1, writeln/2, or using the %n format with printf/2, printf/3. All those will produce a LF or CRLF sequence, depending on the stream property settings (see set_stream_property/3).

11.2.5  General Parsing and Text Generation

Reading and writing of I/O formats that cannot be handled by the methods discussed above are probably best done using Definite Clause Grammar (DCG) rules. See chapter 13.3 for details.

11.2.6  Flushing

On most devices, output is buffered, i.e., any output does not appear immediately on the file, pipe or socket, but goes into a buffer first. To make sure the data is actually written to the device, the stream usually has to be flushed using flush/1. If this is forgotten, the receiving end of a pipe or socket may hang in a blocking read operation.

It is possible to configure a stream such that it is automatically flushed at every line end (see set_stream_property/3).

11.2.7  Prompting

Input streams on terminals can be configured to print a prompt whenever input is required, see set_stream_property/3.

11.2.8  Positioning

Streams that are opened on files or strings can be positioned, i.e., the read/write position can be moved forward or backwards. This is not possible on pipes, sockets, queues and terminals.

To specify a position in the file to write to or read from, the predicate seek/2 is provided. The call

seek(Stream, Pointer)

moves the current position in the file (the ’file pointer’) to the offset Pointer (a number specifying the length in bytes) from the start of the file. If Pointer is the atom end_of_file, the current position is moved to the end of the file. Hence a file could be open in append mode using

open(File, update, Stream), seek(Stream, end_of_file)

The current position in a file may be found by the predicate at/2. The call

at(Stream, Pointer)

unifies Pointer with the current position in the file. The predicate

at_eof(Stream)

succeeds if the current position in the given stream is at the file end.


Previous Up Next