There are two separate sets of stream classes, one for streams on collections, and one for streams on files.
Streams on collections are instances of ReadStream, WriteStream, or ReadWriteStream, which are subclasses of PositionableStream, which is a subclass of Stream. The other subclass of Stream is EsRandom.
Streams on files are instances of ReadFileStream, WriteFileStream, or ReadWriteFileStream, which are subclasses of FileStream.
The two stream hierarchies are not physically related, as was shown in the table of contents for this chapter, but they work on the same principles and respond to almost the same set of messages, differing only in the kinds of objects that can be used, and differences due to the objects, files, or collections, over which the stream operates.
All streams respond to three messages.
atEnd Has the final item been consumed? do: Iterate through a stream next Answer the next item in the streamThese are the only messages that random number streams answer to.
Streams provide an abstraction that enables store and retrieve operations to be performed on the elements of an underlying composite data structure, usually an indexed collection. In particular, a stream remembers the position of the last element in the data structure to be read or written. Streams are positionable, that is, the current read/write position can be changed using the stream's public interface. This allows elements to be accessed in either a sequential or non-sequential fashion.
It is implicit in stream semantics that the elements of the underlying data structure are sequentially ordered. With respect to the Common Language Data Type classes, a ReadWriteStream or WriteStream can stream over an Array, a String, or a DBString(characters in the range 0 to 65535, that is, double-byte (DB) charaters). A ReadStream can stream over the same set of classes plus OrderedCollection, SortedCollection, or Symbol.
Positionable streams have a collection across which they stream, starting with the first element and continuing to the last, unless explicitly changed. Streams keep an internal position for the previous element. Positionable streams can be opened on any kind of indexed collection.
atEnd Has the final item been consumed? close Close the stream contents Answer the whole contents of the stream isEmpty Is the stream empty? position Get the current position of the stream position: n Set the current position of the stream reset Set the position to the front of the collection setToEnd Set the position to the end of the collection size Answer size of file in bytes skip: n Skip the next n elements upToEnd Answer all items up to the stream's end
ReadStream on: 'Mary had a little laptop, its screen was white as snow'Items are taken (read) from the stream with these messages:
copyFrom:to: Copy into new collection do: Iterate through a stream; never terminates next Answer the next element next: n Answer the next n elements as a collection nextLine Answer up to the next line delimiter nextMatchFor: anObjec Answer all elements up to anObject peek Peek at next element peekFor: Compare next element with an object skip: n Skip n items skipTo: item Skip items to the next occurrence of item skipToAll: aCollection Skip items to aCollection upTo: item Return items up to next occurrence of item upToAll: aCollection Skip items up to aCollection upToEnd Get elements up to the endThe next message is used when the stream needs to be processed one item at a time.
- To get the next character from a read stream:
| rs | rs := ReadStream on: 'Mary had a little laptop, its screen was white as snow'. ^rs next Answers: $M- To get the next word from a read stream:
| rs | rs := ReadStream on: 'Mary had a little laptop, its screen was white as snow'. ^rs upTo: $ "Get up to the next blank" Answers: 'Mary'- To get the next line from a read stream:
| rs | rs := ReadStream on: 'Mary had a little laptop,',LineDelimiter,'its screen was white as snow'. ^rs nextLine Answers: 'Mary had a little laptop,'
WriteStream on: String newor:
WriteStream on: Array newThe output collection will be automatically extended when it gets full.
Items are written to the stream with messages such as:
flush Flush buffers to disk nextPut: item Put a single item nextPutAll: collection Put a collection next: n put: item Put n copies of item cr Put a line delimiter space Put a space tab Put a tabThe next example writes two lines to a stream, and then uses the contents message to retrieve a copy of the elements written to the stream.
- Simple Write Stream
| ws | ws := WriteStream on: String new. ws nextPutAll: 'Mary had a little laptop,'; cr. ws nextPutAll: 'it''s screen was white as snow;'; cr. ws nextPutAll: 'and everywhere that Mary went;'; cr. ws nextPutAll: 'her laptop was sure to go.'; cr. ^ ws contents - Answers: 'Mary had a little laptop, it's screen was white as snow; and everywhere that Mary went, her laptop was sure to go.'
ReadWriteStream answers to the same messages as ReadStream and WriteStream, as well as the truncating message:
copyFrom:to: Copy into new collection cr Put a line delimiter do: Iterate through a stream; never terminates flush Flush buffers to disk next Answer the next element next: n Answer the next n elements as a collection nextLine Answer up to the next line delimiter nextMatchFor: anObject Answer all elements up to anObject nextPut: item Put a single item nextPutAll: collection Put a collection next: n put: item Put n copies of item peek Peek at next element peekFor: Compare next element with an object skip: n Skip n items skipTo: item Skip items to the next occurrence of item skipToAll: aCollection Skip items to aCollection space Put a space tab Put a tab truncate truncate objects? upTo: item Return items up to next occurrence of item upToAll: aCollection Skip items up to aCollection upToEnd Get elements up to the end
Random streams answer to these messages:
atEnd Always answers false do: Iterate through a stream; never terminates next Answer the next random numberAs a Random Stream example, shuffle a 'deck' of 'cards' using random numbers. The cards are represented by integers from 1 to 52 in an ordered collection; removal is random. Removed cards are placed into a second array.
Shuffle a deck of cards | deck rand shuf n | rand := EsRandom new. Get new generator. deck := (1 to: 52) as OrderedCollection. Init deck to integers 1 to 52 shuf := OrderedCollection new: deck size. Put shuffled cards here deck size timesRepeat: [ Loop on initial deck size n := (deck size * rand next) floor asInteger + 1. Rand range 1 to cur deck size shuf addLast: (deck removeAtIndex: n) ]. Move card to shuffled deck shuf size = 52 ifFalse: [self error: 'This better not ever happen!' ]. ^shuf
A string representing the current line delimiter can be obtained by sending the lineDelimiter message to a file stream instance, and can be changed to an arbitrary string by sending the lineDelimiter: message. This makes it possible to adopt a single platform's file convention as the standard for all platforms, or to use nextLine to read files written on other platforms.
The line delimiters can be changed in various ways to match other supported platforms (by name) or to other sequences. The line delimiters are in the pool dictionary CldtConstants. The default is named LineDelimiter.
Line delimiters are set with the lineDelimiter: message to a stream.
The two examples below are the same, provided that the default line delimiter has not been changed.
aStream cr. "Output a line delimiter" aStream nextPutAll: LineDelimiter "Output a line delimiter"The next example demonstrates the use of the lineDelimiter: message, as well as their effect on the cr message:
To set to the default line delimiter: aStream lineDelimiter: LineDelimiter. cr; nextPutAll: '<-default line delimiter'.
There are three kinds of file streams:
ReadFileStream Read files only WriteFileStream Write files only ReadWriteFileStream Reads and write files
The open: message opens an existing file, while the openEmpty: message truncates an existing file (to size 0) or creates the file if it does not exist.
Generally, open: is used when reading files and openEmpty: is used when writing new files or overwriting existing files.
Here is an example that illustrates how to open an existing file for reading and properly check for open errors.
Open existing file for reading; check for open errors | file | (file := ReadFileStream open: 'existing.txt') isError ifTrue: [ ^self error: file message ]. "...use the file stream for reading..." file close. "when done, close the file stream."Here is an example that illustrates how to open an existing file for reading and writing and properly check for open errors.
Open existing file for reading and writing, or creates the file if it doesn't exist; check for open errors. | file | (file := ReadWriteFileStream open: 'existing.txt') isError ifTrue: [ ^self error: file message ]. "...use the file stream for reading and/or writing..." file close.Here is an example that illustrates how to open an existing file for writing only, using the openEmpty: message, and properly check for open errors.
Open file for writing (empty it if it exists); check for open errors. | file | (file := WriteFileStream openEmpty: 'new.txt') isError ifTrue: [ ^self error: file message ]. "...use the file stream for writing..." file closeOnce a file stream is open, it is operated upon with the same set of messages used for streams, including, for reading:
next Answer the next element next: n Answer the next n elements as a collection nextLine Answer up to the next line delimiter nextMatchFor: anObject Answer all elements up to anObject skip: n Skip n items skipTo: item Skip items to the next occurrence of item skipToAll: aCollection Skip items to aCollection upTo: item Return items up to next occurrences of item upToAll: aCollection Skip items up to aCollectionand for writing:
nextPut: item Put a single item nextPutAll: collection Put a collection next: n put: item Put n copies of item cr Put a line delimiter space Put a space tab Put a tabOnce all desired operations have been performed, the file stream instance must be closed by sending it the close message before it is discarded. This closes the file, by deallocating any operating system resources associated with the file stream, and flushing any cached data to disk.
On double-byte platforms, the platform character encoding does not necessarily match the character encoding used within Smalltalk. As a result, Smalltalk strings must be converted to and from the platform representation as they are written to and read from files. When the Smalltalk and platform encodings differ, the stream answered by the open: and openEmpty: messages will not be an instance of the class to which the message was sent. In such cases the open: and openEmpty: messages answer a specialized stream that conforms to the requested protocols and manages the conversion of Smalltalk strings to the appropriate platform representation.
In these cases, it is important to use the isError message to test the result of the open: and openEmpty: operations rather than testing the class of the returned object.
Simple Write File Stream | ws text | (ws := ReadWriteFileStream openEmpty: 'maryslap.txt') isError ifTrue: [ ^self error: ws message ]. ws nextPutAll: 'Mary had a little lamp,'; cr. ws nextPutAll: 'it''s light was white as show;'; cr. ws nextPutAll: 'and everywhere that Mary went;'; cr. ws nextPutAll: 'that lamp was sure to glow.'; cr. text := ws contents. ws close. ^ text -Answers: 'Mary had a little lamp, it's light was white as snow; and everywhere that Mary went, that lamp was sure to glow.'The next example shows how to open an existing file for reading, another for writing, and then write the input file's entire contents to the output file. After two opens, the expression old contents reads the whole contents of the open file old into memory; then nextPutAll: outputs it all to new.
Copy a file all at once; prompt for file names | old new | (old := ReadFileStream open: (CxFileSelectionPrompter new prompt)) isError ifTrue: [ ^self error: old message ]. (new := WriteFileStream openEmpty: (System prompt:'Output file name')) isError ifTrue: [^self error: new message ]. new nextPutAll: old contents. old close. new close.The next example reads a text file and changes the first lower case character which follows a period, possibly with separating white space, into an upper case character. It will take text such as:
the dog ran up the tree. the cat barked. the boy laughed.and convert it to:
The dog ran up the tree. The cat barked. The boy laughed.
Capitalize first letter past a period in a file. | input output str period | period := true. str := CwFileSelectionPrompter new title: 'Select input file'; (input := ReadFileStream open: str) isError ifTrue: [ ^self error: input message ]. str := System prompt: 'Enter name of output file'. (output := WriteFileStream open: str) isError ifTrue: [ ^self error: output message ]. [input atEnd ] whileFalse: [ |char| char := input next. period & char isLowercase ifTure: [ output nextPut: char asUppercase ] ifFalse: [output nextPut: char ]. char isSeparator ifFalse: [ period := char == $.] ]. input close. output close.
Go to
Glossary
Return to
Chapter7: Collections
Return to Main
Page