Scheme has input/output (I/O) procedures that will let you read from an input port or write to an output port. Ports can be associated with the console, files or strings.
Scheme’s reader procedures take an optional input port argument. If the port is not specified, the current input port (usually the console) is assumed.
Reading can be character-, line- or s-expression-based.
Each time a read is performed, the port’s state changes so
that the next read will read material following what was
already read. If the port has no more material to be read,
the reader procedure returns a specific datum called the
end-of-file or eof object. This datum is the only value
that satisfies the eof‑object?
predicate.
The procedure read‑char
reads the next character from
the port. read‑line
reads the next line, returning it
as a string (the final newline is not included). The
procedure read
reads the next s-expression.
Scheme’s writer procedures take the object that is to be written and an optional output port argument. If the port is not specified, the current output port (usually the console) is assumed.
Writing can be character- or s-expression-based.
The procedure write‑char
writes the given character
(without the #\
) to the output port.
The procedures write
and display
both
write the given s-expression to the port, with one
difference: write
attempts to use a machine-readable
format and display
doesn’t. E.g., write
uses double
quotes for strings and the #\
syntax for characters.
display
doesn’t.
The procedure newline
starts a new line on
the output port.
Scheme’s I/O procedures do not need a port argument if the
port happens to be standard input or standard output.
However, if you need these ports explicitly, the
zero-argument procedures current‑input‑port
and
current‑output‑port
furnish them. Thus,
(display 9) (display 9 (current-output-port))
have the same behavior.
A port is associated with a file by opening the file.
The procedure open‑input‑file
takes a filename argument
and returns a new input port associated with it. The
procedure open‑output‑file
takes a filename argument and
returns a new output port associated with it. It is an
error to open an input file that doesn’t exist, or to open
an output file that already exists.
After you have performed I/O on a port, you should close it
with close‑input‑port
or close‑output‑port
.
In the following, assume the file hello.txt
contains the
single word hello
.
(define i (open-input-file "hello.txt")) (read-char i) => #\h (define j (read i)) j => ello
Assume the file greeting.txt
does not exist before the
following programs are fed to the listener:
(define o (open-output-file "greeting.txt")) (display "hello" o) (write-char #\space o) (display 'world o) (newline o) (close-output-port o)
The file greeting.txt
will now contain the
line:
hello world
Scheme supplies the procedures call‑with‑input‑file
and
call‑with‑output‑file
that will take care of opening a
port and closing it after you’re done with it.
The procedure call‑with‑input‑file
takes a filename
argument and a procedure. The procedure is applied to an
input port opened on the file. When the procedure
completes, its result is returned after ensuring that the
port is closed.
(call-with-input-file "hello.txt" (lambda (i) (let* ((a (read-char i)) (b (read-char i)) (c (read-char i))) (list a b c)))) => (#\h #\e #\l)
The procedure call‑with‑output‑file
does the analogous
services for an output file.
It is often convenient to associate ports with strings.
Thus, the procedure open‑input‑string
associates a port
with a given string. Reader procedures on this port will
read off the string:
(define i (open-input-string "hello world")) (read-char i) => #\h (read i) => ello (read i) => world
The procedure open‑output‑string
creates an
output port that will eventually be used to create a string:
(define o (open-output-string)) (write 'hello o) (write-char #\, o) (display " " o) (display "world" o)
You can now use the procedure get‑output‑string
to get
the accumulated string in the string port o
:
(get-output-string o) => "hello, world"
String ports need not be explicitly closed.
We have already seen the procedure load
that loads
files containing Scheme code. Loading a file
consists in evaluating in sequence every Scheme form in
the file. The pathname argument given to load
is
reckoned relative to the current working directory of
Scheme, which is normally the directory in which the
Scheme executable was called.
Files can load other files, and this is useful in a
large program spanning many files. Unfortunately,
unless full pathnames are used, the argument file of a
load
is dependent on Scheme’s current
directory. Supplying full pathnames is not always
convenient, because we would like to move the program
files as a unit (preserving their relative pathnames),
perhaps to many different machines.
MzScheme provides the load‑relative
procedure that
greatly helps in fixing the files to be loaded.
load‑relative
, like load
, takes a pathname
argument. When a load‑relative
call occurs in a
file foo.scm
, the path of its argument is reckoned
from the directory of the calling file foo.scm
. In
particular, this pathname is reckoned independent of
Scheme’s current directory, and thus allows convenient
multifile program development.