10.2 Communication via Streams
The contents of a stream may be interpreted in one of the
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.
10.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 ASCII code of the character.
If the end of file has been reached then an exception is raised.
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.
The predicates
read_token/2 and
read_token/3
read_token(Token, Class)
read_token(Stream, Token, Class)
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-ins
read_string/3 and
read_string/4
read_string(Delimiters, Length, String)
read_string(Stream, Delimiters, Length, String)
Here, the input is read up to a specified delimiter or up to a specified
length, 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, _Length, String).
Once a string has been read, string manipulation predicates like
split_string/4
can be used to break it up into smaller components.
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 10.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 19).
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).
10.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 12.3 for details.
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).
Input streams on terminals can be configured to print a prompt
whenever input is required, see
set_stream_property/3.
Streams that are opened on files or strings can be positioned,
ie. 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.