4.16 Input and output

SWI-Prolog provides two different packages for input and output. The native I/O system is based on the ISO standard predicates open/3, close/1 and friends.34Actually based on Quintus Prolog, providing this interface before the ISO standard existed. Being more widely portable and equipped with a clearer and more robust specification, new code is encouraged to use these predicates for manipulation of I/O streams.

Section 4.16.2 describes tell/1, see/1 and friends, providing I/O in the spirit of the outdated Edinburgh standard. These predicates are layered on top of the ISO predicates. Both packages are fully integrated; the user may switch freely between them.

4.16.1 ISO Input and Output Streams

The predicates described in this section provide ISO compliant I/O, where streams are explicitly created using the predicate open/3. The resulting stream identifier is then passed as a parameter to the reading and writing predicates to specify the source or destination of the data.

This schema is not vulnerable to filename and stream ambiguities as well as changes to the working directory. New code is advised to use these predicates to manage input and output streams.

open(+SrcDest, +Mode, -Stream, +Options)
ISO compliant predicate to open a stream. SrcDes is either an atom, specifying a file, or a term `pipe(Command)', like see/1 and tell/1. Mode is one of read, write, append or update. Mode append opens the file for writing, positioning the file-pointer at the end. Mode update opens the file for writing, positioning the file-pointer at the beginning of the file without truncating the file. Stream is either a variable, in which case it is bound to an integer identifying the stream, or an atom, in which case this atom will be the stream identifier.35New code should use the alias(Alias) option for compatibility to the ISO standard The Options list can contain the following options:
type(Type)
Using type text (default), Prolog will write a text-file in an operating-system compatible way. Using type binary the bytes will be read or written without any translation. See also the option encoding.
alias(Atom)
Gives the stream a name. Below is an example. Be careful with this option as stream-names are global. See also set_stream/2.
?- open(data, read, Fd, [alias(input)]).

        ...,
        read(input, Term),
        ...
encoding(Encoding)
Define the encoding used for reading and writing text to this stream. The default encoding for type text is derived from the Prolog flag encoding. For binary streams the default encoding is octet. For details on encoding issues, see section 2.17.1.
bom(Bool)
Check for a BOM (Byte Order Marker) or write one. If omitted, the default is true for mode read and false for mode write. See also stream_property/2 and especially section 2.17.1.1 for a discussion on this feature.
eof_action(Action)
Defines what happens if the end of the input stream is reached. Action eof_code makes get0/1 and friends return -1 and read/1 and friends return the atom end_of_file. Repetitive reading keeps yielding the same result. Action error is like eof_code, but repetitive reading will raise an error. With action reset, Prolog will examine the file again and return more data if the file has grown.
buffer(Buffering)
Defines output buffering. The atom full (default) defines full buffering, line buffering by line, and false implies the stream is fully unbuffered. Smaller buffering is useful if another process or the user is waiting for the output as it is being produced. See also flush_output/[0,1]. This option is not an ISO option.
close_on_abort(Bool)
If true (default), the stream is closed on an abort (see abort/0). If false, the stream is not closed. If it is an output stream, it will be flushed however. Useful for logfiles and if the stream is associated to a process (using the pipe/1 construct).
lock(LockingMode)
Try to obtain a lock on the open file. Default is none, which does not lock the file. The value read or shared means other processes may read the file, but not write it. The value write or exclusive means no other process may read or write the file.

Locks are acquired through the POSIX function fcntl() using the command F_SETLKW, which makes a blocked call wait for the lock to be released. Please note that fcntl() locks are advisory and therefore only other applications using the same advisory locks honour your lock. As there are many issues around locking in Unix, especially related to NFS (network file system), please study the fcntl() manual page before trusting your locks!

The lock option is a SWI-Prolog extension.

The option reposition is not supported in SWI-Prolog. All streams connected to a file may be repositioned.

open(+SrcDest, +Mode, ?Stream)
Equivalent to open/4 with an empty option-list.
open_null_stream(?Stream)
Open a stream that produces no output. All counting functions are enabled on such a stream. An attempt to read from a null-stream will immediately signal end-of-file. Similar to Unix /dev/null. Stream can be an atom, giving the null-stream an alias name.
close(+Stream)
Close the specified stream. If Stream is not open an error message is displayed. If the closed stream is the current input or output stream the terminal is made the current input or output.
close(+Stream, +Options)
Provides close(Stream, [force(true)]) as the only option. Called this way, any resource error (such as write-errors while flushing the output buffer) are ignored.
stream_property(?Stream, ?StreamProperty)
ISO compatible predicate for querying status of open I/O streams. StreamProperty is one of:
alias(Atom)
If Atom is bound, test of the stream has the specified alias. Otherwise unify Atom with the first alias of the stream.bugBacktracking does not give other aliases.
buffer(Buffering)
SWI-Prolog extension to query the buffering mode of this stream. Buffering is one of full, line or false. See also open/4.
bom(Bool)
If present and true, a BOM (Byte Order Mark) was detected while opening the file for reading or a BOM was written while opening the stream. See section 2.17.1.1 for details.
encoding(Encoding)
Query the encoding used for text. See section 2.17.1 for an overview of wide character and encoding issues in SWI-Prolog.
end_of_stream(E)
If Stream is an input stream, unify E with one of the atoms not, at or past. See also at_end_of_stream/[0,1].
eof_action(A)
Unify A with one of eof_code, reset or error. See open/4 for details.
file_name(Atom)
If Stream is associated to a file, unify Atom to the name of this file.
file_no(Integer)
If the stream is associated with a POSIX file-descriptor, unify Integer with the descriptor number. SWI-Prolog extension used primarily for integration with foreign code. See also Sfileno() from SWI-Stream.h.
input
True if Stream has mode read.
mode(IOMode)
Unify IOMode to the mode given to open/4 for opening the stream. Values are: read, write, append and the SWI-Prolog extension update.
output
True if Stream has mode write, append or update.
position(Term)
Unify Term with the current stream-position. A stream-position is an opaque term whose fields can be extracted using stream_position_data/3. See also set_stream_position/2.
reposition(Bool)
Unify Bool with true if the position of the stream can be set (see seek/4). It is assumed the position can be set if the stream has a seek-function and is not based on a POSIX file-descriptor that is not associated to a regular file.
representation_errors(Mode)
Determines behaviour of character output if the stream cannot represent a character. For example, an ISO Latin-1 stream cannot represent cyrillic characters. The behaviour is one of error (throw and I/O error exception), prolog (write \...\ escape code or xml (write &#...; XML character entity). The initial mode is prolog for the user streams and error for all other streams. See also section 2.17.1 and set_stream/2.
type(T)
Unify Bool with text or binary.
tty(Bool)
This property is reported with Bool equals true if the stream is associated with a terminal. See also set_stream/2.
current_stream(?Object, ?Mode, ?Stream)
The predicate current_stream/3 is used to access the status of a stream as well as to generate all open streams. Object is the name of the file opened if the stream refers to an open file, an integer file-descriptor if the stream encapsulates an operating-system stream or the atom [] if the stream refers to some other object. Mode is one of read or write.
is_stream(+Term)
True if Term is a stream name or valid stream handle. This predicate realises a safe test for the existence of a stream alias or handle.
set_stream_position(+Stream, +Pos)
Set the current position of Stream to Pos. Pos is a term as returned by stream_property/2 using the position(Pos) property. See also seek/4.
stream_position_data(?Field, +Position, -Data)
Extracts information from the opaque stream position term as returned by stream_property/2 requesting the position(Position) property. Field is one of line_count, line_position, char_count or byte_count. See also line_count/2, line_position/2, character_count/2 and byte_count/2.36Introduced in version 5.6.4 after extending the position term with a byte-count. Compatible with SICStus Prolog.
seek(+Stream, +Offset, +Method, -NewLocation)
Reposition the current point of the given Stream. Method is one of bof, current or eof, indicating positioning relative to the start, current point or end of the underlying object. NewLocation is unified with the new offset, relative to the start of the stream.

Positions are counted in `units'. A unit is 1 byte, except for text-files using 2-byte Unicode encoding (2 bytes) or wchar encoding (sizeof(wchar_t)). The latter guarantees comfortable interaction with wide-character text-objects. Otherwise, the use of seek/4 on non-binary files (see open/4) is of limited use, especially when using multi-byte text-encodings (e.g. UTF-8) or multi-byte newline files (e.g. DOS/Windows). On text-files, SWI-Prolog offers reliable backup to an old position using stream_property/2 and set_stream_position/2. Skipping N character codes is achieved calling get_code/2 N times or using copy_stream_data/3, directing the output to a null-stream (see open_null_stream/1). If the seek modifies the current location, the line number and character position in the line are set to 0.

If the stream cannot be repositioned, a reposition error is raised. The predicate seek/4 is compatible to Quintus Prolog, though the error conditions and signalling is ISO compliant. See also stream_property/2 and set_stream_position/2.

set_stream(+Stream, +Attribute)
Modify an attribute of an existing stream. Attribute specifies the stream property to set. See also stream_property/2 and open/4.
alias(AliasName)
Set the alias of an already created stream. If AliasName is the name of one of the standard streams is used, this stream is rebound. Thus, set_stream(S, current_input) is the same as set_input/1 and by setting the alias of a stream to user_input, etc. all user terminal input is read from this stream. See also interactor/0.
buffer(Buffering)
Set the buffering mode of an already created stream. Buffering is one of full, line or false.
close_on_abort(Bool)
Determine whether or not the stream is closed by abort/0. By default streams are closed.
encoding(Atom)
Defines the mapping between bytes and character codes used for the stream. See section 2.17.1 for supported encodings.
eof_action(Action)
Set end-of-file handling to one of eof_code, reset or error.
timeout(Seconds)
This option can be used to make streams generate an exception if it takes longer than Seconds before any new data arrives at the stream. The value infinite (default) makes the stream block indefinitely. Like wait_for_input/3, this call only applies to streams that support the select() system call. For further information about timeout handling, see wait_for_input/3. The exception is of the form
error(timeout_error(read, Stream), _)
record_position(Bool)
Do/do not record the line-count and line-position (see line_count/2 and line_position/2).
representation_errors(Mode)
Change the behaviour when writing characters to the stream that cannot be represented by the encoding. See also stream_property/2 and section 2.17.1.
file_name(FileName)
Set the file name associated to this stream. This call can be used to set the file for error-locations if Stream corresponds to FileName and is not obtained by opening the file directly but, for example, through a network service.
tty(Bool)
Modify whether Prolog thinks there is a terminal (i.e. human interaction) connected to this stream. On Unix systems the initial value comes from isatty(). On Windows, the initial user streams are supposed to be associated to a terminal. See also stream_property/2.
set_prolog_IO(+In, +Out, +Error)
Prepare the given streams for interactive behaviour normally associated to the terminal. In becomes the user_input and current_input of the calling thread. Out becomes user_output and current_output. If Error equals Out an unbuffered stream is associated to the same destination and linked to user_error. Otherwise Error is used for user_error. Output buffering for Out is set to line and buffering on Error is disabled. See also prolog/0 and set_stream/2. The clib package provides the library library(prolog_server) creating a TCP/IP server for creating an interactive session to Prolog.

4.16.2 Edinburgh-style I/O

The package for implicit input and output destination is (almost) compatible to Edinburgh DEC-10 and C-Prolog. The reading and writing predicates refer to resp. the current input- and output stream. Initially these streams are connected to the terminal. The current output stream is changed using tell/1 or append/1. The current input stream is changed using see/1. The streams current value can be obtained using telling/1 for output- and seeing/1 for input streams.

Source and destination are either a file, user, or a term `pipe(Command)'. The reserved stream name user refers to the terminal.37The ISO I/O layer uses user_input, user_output and user_error. In the predicate descriptions below we will call the source/destination argument `SrcDest'. Below are some examples of source/destination specifications.

?- see(data). % Start reading from file `data'.
?- tell(user). % Start writing to the terminal.
?- tell(pipe(lpr)). % Start writing to the printer.

Another example of using the pipe/1 construct is shown below.38As of version 5.3.15, the pipe construct is supported in the MS-Windows version, both for plcon.exe and plwin.exe. The implementation uses code from the LUA programming language (http://www.lua.org). Note that the pipe/1 construct is not part of Prolog's standard I/O repertoire.

getwd(Wd) :-
        seeing(Old), see(pipe(pwd)), 
        collect_wd(String), 
        seen, see(Old), 
        atom_codes(Wd, String).

collect_wd([C|R]) :-
        get0(C), C \== -1, !, 
        collect_wd(R).
collect_wd([]).

Compatibility notes

Unlike Edinburgh Prolog systems, telling/1 and seeing/1 do not return the filename of the current input/output, but the stream-identifier, to ensure the design pattern below works under all circumstances.39Filenames can be ambiguous and SWI-Prolog streams can refer to much more than just files.

        ...,
        telling(Old), tell(x),
        ...,
        told, tell(Old),
        ...,

The predicates tell/1 and see/1 first check for user, the pipe(command) and a stream-handle. Otherwise, if the argument is an atom it is first compared to open streams associated to a file with exactly the same name. If such a stream, created using tell/1 or see/1 exists, output (input) is switch to the open stream. Otherwise a file with the specified name is opened.

The behaviour is compatible to Edinburgh Prolog. This is not without problems. Changing directory, non-file streams, multiple names referring to the same file easily lead to unexpected behaviour. New code, especially when managing multiple I/O channels should consider using the ISO I/O predicates defined in section 4.16.1.

see(+SrcDest)
Open SrcDest for reading and make it the current input (see set_input/1). If SrcDest is a stream-handle, just makes this stream the current input. See the introduction of section 4.16.2 for details.
tell(+SrcDest)
Open SrcDest for writing and make it the current output (see set_output/1). If SrcDest is a stream-handle, just makes this stream the current output. See the introduction of section 4.16.2 for details.
append(+File)
Similar to tell/1, but positions the file pointer at the end of File rather than truncating an existing file. The pipe construct is not accepted by this predicate.
seeing(?SrcDest)
Same as current_input/1, except that user is returned if the current input is the stream user_input to improve compatibility with traditional Edinburgh I/O. See the introduction of section 4.16.2 for details.
telling(?SrcDest)
Same as current_output/1, except that user is returned if the current output is the stream user_output to improve compatibility with traditional Edinburgh I/O. See the introduction of section 4.16.2 for details.
seen
Close the current input stream. The new input stream becomes user_input.
told
Close the current output stream. The new output stream becomes user_output.

4.16.3 Switching Between Edinburgh and ISO I/O

The predicates below can be used for switching between the implicit- and the explicit stream based I/O predicates.

set_input(+Stream)
Set the current input stream to become Stream. Thus, open(file, read, Stream), set_input(Stream) is equivalent to see(file).
set_output(+Stream)
Set the current output stream to become Stream. See also with_output_to/2.
current_input(-Stream)
Get the current input stream. Useful to get access to the status predicates associated with streams.
current_output(-Stream)
Get the current output stream.

4.16.4 Write onto atoms, code-lists, etc.

with_output_to(+Output, :Goal)
Run Goal as once/1, while characters written to the current output is sent to Output. The predicate is SWI-Prolog specific, inspired by various posts to the mailinglist. It provides a flexible replacement for predicates such as sformat/3, swritef/3, term_to_atom/2, atom_number/2 converting numbers to atoms, etc. The predicate format/3 accepts the same terms as output argument.

Applications should generally avoid creating atoms by breaking and concatenating other atoms as the creation of large numbers of intermediate atoms generally leads to poor performance, even more so in multi-threaded applications. This predicate supports creating difference-lists from character data efficiently. The example below defines the DCG rule term//1 to insert a term in the output:

term(Term, In, Tail) :-
        with_output_to(codes(In, Tail), write(Term)).

?- phrase(term(hello), X).

X = [104, 101, 108, 108, 111]
A Stream handle or alias
Temporary switch current output to the given stream. Redirection using with_output_to/2 guarantees the original output is restored, also if Goal fails or raises an exception. See also call_cleanup/2.
atom(-Atom)
Create an atom from the emitted characters. Please note the remark above.
string(-String)
Create a string-object as defined in section 4.23.
codes(-Codes)
Create a list of character codes from the emitted characters, similar to atom_codes/2.
codes(-Codes, -Tail)
Create a list of character codes as a difference-list.
chars(-Chars)
Create a list of one-character-atoms codes from the emitted characters, similar to atom_chars/2.
chars(-Chars, -Tail)
Create a list of one-character-atoms as a difference-list.