Note: Much of this information is borrowed from the Basis Library. Please see the Basis
Library for full specifications.
To be able to read from and write to files we need functions that will enable us
to open and close files and either input information from the file or output to
the file. Files are being manipulated using file handles, called streams.
For now think of streams as sequences of elements (for files, sequences of bytes
or characters). The built-in IMPERATIVE_IO
signature defines
operations to open files, read/write files, and close files. The signature
also defines two types called instream
and outstream
. An
instream
(input streams) is a type that allows you to input or read the elements of the stream one
at a time and an outstream
(output streams) allows you to output or write
elements to a stream one at a time.
signature IMPERATIVE_IO = sig type vector type elem type instream type outstream
val input1 : instream -> elem option val inputN : (instream * int) -> vector val inputAll : instream -> vector val canInput : (instream * int) -> int option val lookahead : instream -> elem option val closeIn : instream -> unit
val output : (outstream * vector) -> unit val output1 : (outstream * elem) -> unit val flushOut : outstream -> unit val closeOut : outstream -> unit ...
end
input1
, inputN
and inputAll
functions are used to read one, N, or all elemens of
a instream; output
and output1 functions are used to write all or one element
to an outstream. Notice that these functions have an imperative behavior (as
indicated by the name of the signature): they do not return a new instream or outstream
(the return type is unit). Instead, the current position in the stream is
"advanced" one step ahead as a side-effect. closeIn
and closeOut
are used to close an instream and outstream,
respectively. Make sure you close streams after you are done reading from or
writing to them. The output streams contain buffers that might not be flushed
out until you close the files (or perform an explicit flushOut
operation).type vector
type elem
These are the abstract types of stream elements and vectors of elements. For
text files like those used in TextIO
, these are Char.char
and
String.string
,
while for binary files like those used in BinIO
, these are Word8.word
and
Word8Vector.vector
.The IMPERATIVE_IO
signature
provide the common part for two other signatures: BIN_IO
(for
binary file manipulations) and TEXT_IO
(for text file
manipulations). These signatures are implemented by structures BinIO
and TextIO
, respectively. Hence, BinIO
and
TextIO
support all of the above functions, plus the ones discussed below.
The BinIO
structure provides support for manipulating binary files. The structure
BinIO
implements the BIN_IO
signature, which extends
the IMPERATIVE_IO
interface with functions for opening files:
include IMPERATIVE_IO val openIn : string -> instream val openOut : string -> outstream val openAppend : string -> outstream
openIn name
openOut name
Open the file named name for binary input (reading) and output (writing), respectively.
If name is a relative pathname, the file opened depends on the current working
directory. On openOut
, the file is created if it does not already exist and
truncated to length zero otherwise. Raises Io if a stream cannot be opened on
the given file or, in the case of openIn
, the file name does not exist.
For binary files the elem
and vector
types are defined using the Word8
structure, as follows:
type vector = Word8Vector.vector type elem = Word8.wordThe type
Word8.word
essentially represents unsigned 8-bit integers.
The Word8
structure implements the WORD
signature, and
provides arithmetic and
logical operations and conversion operations (e.g. bitwise and, bitwise or,
bitwise shifts) for this type. They are meant to give efficient access to the primitive
machine word types of the underlying hardware. (The structures such as Int8
or Int32
implement the INTEGER
signature and model signed integers.)
The TextIO
structure provides support for manipulating text files.
The structure also defines functions for opening text files. In addition, it defines
the standard in, standard out, and standard error streams, and provides some
functions specific to text files (such as reading a line from an input stream).
The structure TextIO
implements the TEXT_IO
signature, which includes the
following:
include IMPERATIVE_IO val inputLine : instream -> string val openIn : string -> instream val openOut : string -> outstream val stdIn : instream val stdOut : outstream val stdErr : outstream val print : string -> unit ...
openIn name
openOut name
inputLine strm
Reads one line from strm and return it. If strm is not at end of file, the result
ends with a newline character. If it is the last line of the file and the file
ends without a trailing newline, stick the newline on anyway. If at end of file,
return the empty string. Raises Size if the length of the line exceeds the
length of the longest string.
val stdIn
These correspond to the standard input, output and error streams, respectively.
val stdOut
val stdErr
print s
prints the string s to the standard output stream and flushes the stream. This
is equivalent to: (output (stdOut, s); flushOut stdOut)
For binary file the elem and vector types are:
type vector = String.string type elem = char
This is a simple example using TextIO that copies the contents of from one
text file, infile
, to another, outfile
:
fun copyTextFile(infile: string, outfile: string) = let val ins = TextIO.openIn infile val outs = TextIO.openOut outfile fun helper(copt: char option) = case copt of NONE => (TextIO.closeIn ins; TextIO.closeOut outs) | SOME(c) => (TextIO.output1(outs,c); helper(TextIO.input1 ins)) in helper(TextIO.input1 ins) end