[prev] [up] [next]

Stream classes

Streams offer a means to sequentially access elements of a collection. Technically they are statefull objects, holding on a collection or file-handle, and a position. The position is advanced when reading/writing. In addition to internal streams (which operate on a collection), there are also external streams to access files, directories, pipes or sockets.

The most useful stream classes are:

Although it is possible to stream any collection, containing any type of elements, the most common use is when streaming over Character elements. Such streams may be either internal streams (reading characters from a string or writing into a string) or external streams (reading/writing files, pipes, sockets etc.).

External streams only support byte-sized elements. These must be either small integers (0..255) or characters in the single-byte range.


ReadStreams can be used to process elements of a collection sequentially.
The typical use of readStreams is:


Separating a string into individual words:

    |string stream word1 word2 num1 num2 num3|

    string := 'one two 12345 567.678'.
    stream := string readStream.

    word1 := stream nextAlphaNumericWord.
    stream skipSeparators.
    word2  := stream upToSeparator.
    num1   := Number readFrom:stream.
    num2   := Number readFrom:stream.
    num3   := Number readFrom:stream onError:[ 'NothingMore' ].

    Transcript showCR:word1.
    Transcript showCR:word2.
    Transcript showCR:num1.
    Transcript showCR:num2.
    Transcript showCR:num3.
notice, that there are other ways to perform the above; tokenization could also be done with:
    |string words word1 word2 num1 num2 num3|

    string := 'one two 12345 567.678'.
    words := string asCollectionOfWords.

    word1  := words first.
    word2  := words at:2.
    num1   := Number readFrom:(words third).
    num2   := Number readFrom:(words at:4).
    num3   := Number readFrom:(words at:5 ifAbsent:nil) onError:[ 'NothingMore' ].

    Transcript showCR:word1.
    Transcript showCR:word2.
    Transcript showCR:num1.
    Transcript showCR:num2.
    Transcript showCR:num3.

More details are found in the "ReadStream class documentation".


WriteStreams can be used to collect elements in a collection.
The typical use of writeStreams is:

More details are found in the "WriteStream class documentation".


This is an abstract class - i.e. there are no instances of ExternalStream. Instead, it provides the common behavior for all streams which are associated with an external (OperatingSystem-) stream. Examples are FileStream, PipeStream, Socket and DirectoryStream.
Therefore, the following protocol is valid for all off the above mentioned classes.
ExternalStreams elements are normally characters, however, they also support a so called binary mode in which byte-valued integers are processed.
Typical operations:

Notice, that in binary mode, externalStreams expect byte-value (0..255) integers in all nextPut:-like messages, and return small integer values in all next-like messages.
Thus, to read the first 5 bytes of a file into a byteArray, use:
    |s bytes|

    s := 'patches' asFilename readStream.
    s binary.
    bytes := s next:5.
    s close.
    bytes inspect.
Also, there a methods to read and write low-level machine objects, such as 2,4 and 8-byte integers. These can be processed both in least-significant-byte-first (LSB) or most-significant-byte-first (MSB) order. All external streams are automatically closed (towards the OperatingSystem) when the stream object is reclaimed by the garbage collector. However, since this may happen at any unspecified later time, it is wise to explicitely close a file when no longer used (to release system resources earlier).
In addition, any writeStream should also be closed as soon as possible, because some written data may remain in internal buffers (and therefore: be not in the file) until closed.

More details are found in the "ExternalStream class documentation".


FileStreams support all of ExternalStreams protocol. They can be created to read, write, readWrite or append from/to a file.

The above was the internal low level instance creation protocol, which is somewhat politically incorrect to use. For portability, please use the companion class Filename to create fileStreams:


Reading a file:

If the file is small (say smaller than a megabyte), it may make sense to read the files contents as a whole into either a collection of lines:

    lines := 'smalltalk.rc' asFilename contents.
or a single (big) string:

    bigString := 'smalltalk.rc' asFilename contents asString.
If the file is big, you may want to enumerate byte-blocks with
    |stream eachLine block|

    stream := 'stx.exe' asFilename readStream.
    [stream atEnd] whileFalse:[
	block := stream nextBytes:(8*1024).
	"/ do something with block here; the last one might be smaller than 8k
	Transcript showCR:block size.
    stream close.
or enumerate individual lines with:
    |stream eachLine|

    stream := 'smalltalk.rc' asFilename readStream.
    [stream atEnd] whileFalse:[
	eachLine := stream nextLine.
	"/ do something with line here
    stream close.
of course, linewise enumeration is also possible in the above (reading all first) case:

    lines := 'smalltalk.rc' asFilename contents.
    lines do:[:eachLine |
	"/ do something with eachLine here

Writing a file:

The following code fragment opens a file and writes a few lines into it:
    |stream eachLine|

    stream := 'newFile' asFilename writeStream.

    stream nextPutLine:'hello - this is line1'.
    stream nextPutAll:'this is line2'.
    stream spaces:8.
    stream nextPutAll:'more into line2'.
    stream cr.
    stream nextPutLine:'this is line3'.
    stream cr.
    stream cr.
    stream cr.
    stream close.

More details are found in the "FileStream class documentation" or "Filename's class documentation".


PipeStreams allow reading the output of any unix command or sending text for input to a unix command. Once created, they behave like any other ExternalStream.

For example, to get a list of users logged in your machine, get the output of the "who" UNIX command:
    |s line|

    s := PipeStream readingFrom:'who'.
    [(line := s nextLine) notNil] whileTrue:[
	Transcript showCR:line
    s close

More details are found in the "PipeStream class documentation".


Sockets allow the sending/receiving of unstructured data via TCP/IP connections.
Due to a somewhat more complex naming and connection procedure, the protocol for Socket creation is a bit more inconvenient than for the above streams; however, for simple client/server TCP connections, an ``easy instance creation'' protocol is provided: after that, communication through the socket object is performed as described above.

See examples in the "doc/coding" directory for more details.

More details are found in the "Socket class documentation".

Copyright 1996 Claus Gittinger Development & Consulting


Doc $Revision: 1.27 $ $Date: 2016/09/14 09:41:12 $