Previous Page TOC Next Page



15. I/O

I/O is implemented in Blue by providing a number of standard classes and objects that can be called to perform input/output operations. There are two groups of I/O classes: one group for text based (or "standard") I/O and one group for more sophisticated graphical user interface building. The graphical user interface classes are found in the Blue GUI library and are not described here. The following sections describe what is known as standard I/O, a simple, text based I/O facility.

15.1 Standard I/O

Blue provides four environment variables for standard I/O: input, output, terminal and format. These variables are predefined and accessible in all Blue classes.

It also provides two predefined objects: one object of class TextTerminal and one object of class OutputFormat. The predefined variables input, output and terminal typically all refer to the predefined TextTerminal object, the format variable refers to the OutputFormat object. It is possible, though, to bind input or output to other objects, for instance output to an object representing a printer or input to a file. This can be done externally before a routine is executed, or dynamically by the program.

The predefined variables are of the following classes:

var

input : IO_Channel
output : IO_Channel
terminal : TextTerminal
format : OutputFormat

IO_Channel is a superclass of TextTerminal, so the variables input and output provide a view onto the predefined TextTerminal object as an IO_Channel object.

The variables input and output exist to provide a means do redirect input or output to and from other channels (such as a text file, which is also a subclass if IO_Channel).

The variable terminal exists so that a program can take advantage of TextTerminal specific operations that are not part of IO_Channel (such as positioning the cursor on screen, etc.)

For a full description and interface, see the "Blue Standard Library Manual" [2].

15.1.1 Standard Output

Output to the terminal is usually done using the alias print.

Examples:

i : Integer

m : Myclass print (i)
print (m)
print ("The value of ", i, " is ", m, "\n")

The alias print is defined as

print (a, b, ...) <=> output.write (str (a, b, ...) )

str itself is an alias, which is defined as

str (a, b, ...) <=> a.toString.concat (b.toString.concat (...))

The function concat is defined in class String and returns a string that is the concatenation of two other strings. The function toString is defined for all predefined classes (except Arrays) and may be defined for user defined classes. It returns a printable representation of an object in a string. If a user defined class wants to define a toString routine, it has to conform to the following signature:

toString -> (s: String)
== Return a string representation of this object.

Every object that defines a function toString conforming to this specification, can be printed using the alias print.

Furthermore, apart from being used for output, the str alias can be used for conversion to string from another type and for concatenation of string representations of any type, including strings.

Examples:

s, s1, s2 : String

i : Integer
m : Myclass s := str (i) - - conversion Integer to String
s := str ("The value is ", i) - - conversion and concatenation
s := str (s1, s2) - - concatenation of Strings

15.1.2 Output Formatting

The toString routines of the predefined classes Integer, Real, Boolean and String use the predefined format object to determine their output format. (format is a predefined variable referring to a standard object of class OutputFormat.) Calls to format may be made to influence the formatting of number, Boolean and String output. The interface of OutputFormat is as follows:

class JustifyValue is Enumeration

manifest LeftJustify, CenterJustify, RightJustify
end class

class interface OutputFormat is

width (n: Integer)
== Set field width for output. Default is 0. If the string
== representation of a value takes more characters than the
== field width, the width is ignored.
== Used by Integer, Real, Boolean and String.
pre
n >= 0

roundTo (n: Integer)
== Round output of reals to n digits after the decimal point.
== Default is 6. Used by Real.
pre
n >= 0

justify (j: JustifyValue)
== Set justification for output if field width is greater than
== output length. Default is LeftJustify.
== Used by Integer, Real, Boolean and String.

getWidth -> (width: Integer)
== Return the current field width
post
width >= 0

getRound -> (digits: Integer)
== Return current round value.
post
digits >= 0

getJustify -> (just: JustifyValue)
== Return current justification
end class

The format object may also be used by user defined classes to determine the behaviour of the toString routine (so that user defined output can, for instance, honour field width or justification).

15.1.3 Standard Input

Input from standard input is defined for integers, reals, booleans and strings. It is done using a group of aliases which map to routine calls to the standard object input. The aliases are:

readInt   <=>   input.readInt

readReal <=> input.readReal
readStr <=> input.readStr
readChar <=> input.readChar

All of these routines are functions returning objects of the according type.

Examples:

num := readInt

name := readStr
line [i] := readChar

readInt and readReal skip whitespace and end-of-line characters. If the next characters can not be interpreted in the required format, they return nil. readStr reads until an end-of-line character is found, or the end of file is reached. The relevant part of the interface of the class IO_Channel is shown below:

class interface IO_Channel is 

...
readStr -> (s : String)
== Read a String. Reads characters until
== a line break ("\n") is read or an error condition (such as
== "end-of-file" is encountered). The resulting String does
== not include the line break character.

readChar -> (s : String)
== Read the next character. Every character (including line
== break characters) are returned as entered. The line break
== character is returned as "\n".

readInt -> (i : Integer)
== Read an Integer. Skips white space (spaces, tabs and
== newlines) before the Integer. Returns "nil" if the
== next non-white characters do not represent a number.
== Numbers are written with an optional - or + sign and digits.

readReal -> (r : Real)
== Read an Real (floating point number). Skips white space
== (spaces, tabs and newlines) before the number. Returns "nil"
== if the next non-white characters cannot be interpreted as a
== number. Real numbers are written with an optional - or +
== sign, digits and a decimal point (.).

endLine
== Read and discard all characters up to (and including) the next
== NewLine character.

end class

15.1.4 The TextTerminal Class

As mentioned above, the variables input, output and terminal by default refer to an object of class TextTerminal, which is created by the Blue environment on system startup (a project does not need to create this object explicitly). The TextTerminal class defines additional routines apart from those defined in IO_Channel. Those routines can be called only through the variable terminal, since it is the only one to be declared of class TextTerminal. There are no aliases for these routines. They are called using the standard routine call syntax. Fir example, to position the cursor on the screen, a class might include the statement

terminal.cursorTo (4, ln)

See Appendix D for the complete interface of class TextTerminal.

15.2 File I/O

Working with files is supported by standard classes, available in the standard library (group Standard_IO). These are the classes FileSystemHandle and TextFileHandle. Objects of both of these classes are handles - they give access to an underlying object. This indicates that, for instance, creating a file system handle does not create a file system, but rather creates an object that gives the user access to the underlying filesystem which exists independently from the handle object. Equally, creating a TextFileHandle does not create a text file - it just allows access to a text file that was created earlier.

Class FileSysHandle

The class FileSysHandle is used to access the file system. Access to the file sytem allows operations such as creating or removing files, reading directories, checking for the existence of files, etc.

The (incomplete) definition of the class FileSysHandle is

class interface FileSysHandle is 

...

creation (fileSys : String)
== Create a handle to a file system. The parameter "fileSys"
== identifies the file system to be accessed. On systems that
== have only one file system (e.g. Unix), the parameter is
== ignored.

routines

createFile (name : String) -> (done : Boolean)
== Create a file with file name "name". If the file exists, its
== length is truncated to 0.
== ...

deleteFile (name : String) -> (done : Boolean)
== Delete the file with file name "name".
== ...

copyFile (name : String, newName : String)
-> (done : Boolean)
== Copy the file with name "name" to a second file with name
== "newName".
== ...

createDirectory (name : String) -> (done : Boolean)
== Create a directory with name "name".
== ...

deleteDirectory (name : String) -> (done : Boolean)
== Delete the directory with name "name". The directory must
== exist and must be empty.
== ...

readDirectory (name : String) -> (dir : Array <String>)
== Read the directory with name "name". Returns an array with
== the directory entries. Each entry is the name of a file or
== directory. ...

exists (name : String) -> (result : Boolean)
== Return result=true if an entry with name "name" exists in
== the file system.

...

end class

This interface is incomplete. Look at the interface online in the Blue class browser, or have a look at "The Blue Libraries" [2] to see the full interface.

Class TextFileHandle

A file handle is used to access the contents of a file. A file handle is not the file itself. This is an important distinction. Creating a file handle does not create a file. Creation of a file handle rather corresponds to opening a file.

Example:

This example creates a copy of an text file.

copy (source: String, target: String) is
      == Copy the file "source" to file "target" character by character.

var
filesys : FileSysHandle
file1 : TextFileHandle
file2 : TextFileHandle
do
filesys := create FileSysHandle (nil)

- - create the target file

if (not filesys.createFile (target)) then
print ("Could not create target file!\n")
return
end if

- - open both files

file1 := create TextFileHandle (source)
file2 := create TextFileHandle (target)

if (not file1.isReadable) or (not file2.isWritable) then
print ("Could not open files with necessary access.\n")
return
end if

- - do the copying

loop
exit on file1.endOfFile
file2.write (file1.readChar)
end loop

- - close the files

file1.close
file2.close
print ("Done.\n")

end copy

After creating a TextFileHandle, the state of the file handle should be checked. A file is always opened for reading and writing, if the underlying file system allows this access. If access rights in the file system deny read or write access, the file is opened in read-only or write-only mode. The access can be checked with the isReadable and isWritable functions. If no access is possible at all (either no access was granted by the file system, or the file does not exist) then isReadable and isWritable are both false, and isBad is true. All attempts to read from a file that is not readable, or to write to a file that is not writable result in runtime errors.

The function endOfFile can be used to check whether the current file position is at the end of the file. It must be checked before a read operation is attempted. Trying to read from a file while endOfFile is true is an error.

The (incomplete) definition of class TextFileHandle is

class interface TextFileHandle is IO_Channel


creation (fileName : String)
== Create a handle to a text file. The file "fileName" must exists
== in the file system.
==
== If a file with the specified name does not exist or if access to
== the file is denied, no file will be opened and the status of the
== TextFileHandle will be set to "isBad" (see routine "isBad"
== below).
== This routine does not create a file. To create a new file, use
== the createFile routine in class FileSystemHandle.
pre
fileName <> nil

routines

inherited from IO_Channel

write (s : String) is redefined
== Write 's' to the file.
pre
isWritable

readStr -> (s : String) is redefined
== Read a String from the file. Reads characters until
== a line break ("\n") is read or the end of the file is reached.
== The resulting string does not include the line break character.
== ...
pre
isReadable

readChar -> (s : String) is redefined
== Read the next character from the file. Every character
== (including line break characters) are returned as entered
== The line break character is returned as "\n".
pre
isReadable

readInt -> (i : Integer) is redefined
== Read an Integer from the file. Skips white space (spaces,
== tabs and newlines) before the Integer. Returns "nil" if the
== next non-white characters do not represent a number.
== Numbers are recognised with an optional - or + sign and
== digits.
pre
isReadable

...

new routines

position -> (pos : Integer)
== Return the read/write position in the file.
== The position is the offset from the beginning of the file.
== The first position (start of file) is 0.
pre
not isBad

setPosition (pos : Integer)
== Set the read/write position in the file to "pos".
== 0 is the beginning of the file, -1 is the end of the file.
== Positive numbers count from the beginning of the file,
== negative numbers count backwards from the end of the file.
pre
not isBad and pos <> nil

endOfFile -> (eof : Boolean)
== Return true if the current file position is at the end of the
== file.
pre
not isBad

close
== Close the file associated with this handle. In general all files
== should be closed by the user as soon as they are not needed
== anymore. Closing files is important, because the number of
== files a user can have open at the same time is limited. If files
== are not closed, opening of further files might fail. The exact
== number of files that can be open at any one time is system
== dependent.
== Also, output to files is buffered. Text written to the file is
== not immediately written to disk. Only after closing the file or
== calling an explicit "flush" can the user rely on the file content
== being visible to other processes.
==
== Files not being closed are automatically closed when the file
== handle object is garbage collected. Since the user has no
== influence on the timing of garbage collection, it is bad practice
== to rely on this.
==
== After closing, the file handle is not usable any more, all read
== and write operations cause runtime errors, and "isBad" returns
== true.

isReadable -> (readable : Boolean)
== Returns true if the file can be read. If access rights in the
== file system specify write-only access, isReadable returns
== false.

isWritable -> (writable : Boolean)
== Returns true if the file can be written. If access rights in the
== file system specify read-only access, isWritable returns false.

isBad -> (bad : Boolean)
== "isBad" returns true if this file handle cannot be used to access
== a file. This can happen if the file specified in the creation
== routine does not exist, or if neither read nor write access was
== permitted. If "isBad" is true, "isReadable" and "isWritable"
== are both false.

end class

This interface is incomplete. Look at the interface online in the Blue class browser, or have a look at "The Blue Libraries" [2] to see the full interface.

Previous Page TOC Next Page