Streams & Files

A stream is a cursor over a sequence (a collection, a file, a socket).

23.1 Streams

A stream is a cursor over a sequence (a collection, a file, a socket). Main classes: ReadStream, WriteStream, ReadWriteStream (all subclasses of PositionableStream).

23.2 Reading

Typical loop: [ stream atEnd ] whileFalse: [ words add: (stream upTo: $ ) ].

| rs |
rs := ReadStream on: #(1 2 3 4 5 6).      "or: #(1 2 3 4 5 6) readStream"
rs next.            "→ 1"
rs next: 2.         "→ #(2 3)"
rs peek.            "→ 4 (doesn't advance)"
rs skip: 1.
rs upToEnd.         "→ #(5 6)"
rs atEnd.           "→ true"
rs reset.           "back to the start"

'name=Ada;age=36' readStream upTo: $=.            "→ 'name'"

23.3 Writing

print: writes an object's printString; << writes strings and the displayString of other objects (Pharo).

| ws |
ws := WriteStream on: (String new: 100).
ws nextPutAll: 'Hello'; nextPut: $,; space; print: 42; cr; tab; nextPutAll: 'done'.
ws contents.        "→ 'Hello, 42
	done'"

ws := WriteStream on: (Array new: 0).
ws nextPut: 1; nextPut: 2.
ws contents.        "→ #(1 2)"

String streamContents: [ :s | s nextPutAll: 'abc'; print: 3 @ 4 ].   "→ 'abc(3@4)'"
Array streamContents: [ :s | 1 to: 3 do: [ :i | s nextPut: i squared ] ].   "→ #(1 4 9)"

23.4 ReadWriteStream

| rws |
rws := ReadWriteStream on: String new.
rws nextPutAll: 'Smalltalk'.
rws reset.
rws next: 5.        "→ 'Small'"

23.5 Files with FileSystem

Pharo's FileSystem library models paths as FileReference objects:

| dir file |
"Roots: FileSystem workingDirectory, FileLocator home, FileLocator imageDirectory, FileLocator temp"
dir := FileLocator home / 'smalltalk-course'.
dir ensureCreateDirectory.
file := dir / 'notes.txt'.                    "the / message builds paths"
'notes.txt' asFileReference.                  "relative to the working directory"

"Write (delete first: writeStream overwrites from the start without truncating)"
file ensureDelete.
file writeStreamDo: [ :stream |
    stream nextPutAll: 'First line'; cr; nextPutAll: 'Second line'; cr ].

"Read"
file contents.                                "→ the whole file as a String (UTF-8 decoded)"
file contents lines.                          "→ array of lines"
file readStreamDo: [ :stream |
    [ stream atEnd ] whileFalse: [ Transcript show: stream nextLine; cr ] ].

"Inspect and manipulate"
file exists.   file size.   file basename.   file extension.   file parent.   file isDirectory.
dir children.   dir files.   dir directories.   dir filesMatching: '*.txt'.
dir allChildren select: [ :f | f extension = 'st' ].
file copyTo: dir / 'backup.txt'.    file moveTo: dir / 'moved.txt'.    (dir / 'moved.txt') delete.
dir deleteAll.           
…

23.6 In-memory file systems

Great for tests — no disk I/O:

| fs |
fs := FileSystem memory.
(fs root / 'a.txt') writeStreamDo: [ :s | s nextPutAll: 'in memory' ].
(fs root / 'a.txt') contents.        "→ 'in memory'"

23.7 CSV

Simple CSV: file contents lines collect: [ :line | line substrings: ',' ]. For robust CSV (quoting, escaping), use NeoCSV (NeoCSVReader on: stream).

23.8 Other dialects

Squeak uses FileDirectory and FileStream (FileStream fileNamed: 'x.txt' do: [...]), and also supports FileSystem in recent versions. GNU Smalltalk uses FileStream open: 'x.txt' mode: FileStream read and File name: 'x.txt'. VisualWorks uses 'x.txt' asFilename readStream.