Files & streams
Read small files, process streams, and release file handles predictably.
On this page
Use valk.fs for filesystem operations. Start with whole-file helpers when the content is small, then use streams when you want to process a bounded amount of data at a time.
Read or write a complete file#
This program writes a local file, then reads it back:
use valk.fs
fn main() {
fs.write("notes.txt", "First note\n") ! {
println("Could not write notes.txt")
return
}
let text = fs.read("notes.txt") ! {
println("Could not read notes.txt")
return
}
println(text)
}
fs.write replaces the file's contents by default. Its optional third argument enables appending: fs.write("notes.txt", "Another note\n", true). fs.read returns the complete contents as a string, so its memory use grows with the file.
Process lines from a stream#
The following reads a notes.txt file in the working directory:
use valk.fs
use valk.io
fn main() {
let file = fs.stream("notes.txt") ! {
println("Could not open notes.txt")
return
}
defer file.close() _
let lines = io.LineReader.new(file)
while true {
let line = lines.read_line() ! {
println("Could not read a line")
return
}
if !isset(line) : break
println(line)
}
}
read_line() removes the line ending and returns null at end of input. A read error is separate from that normal end condition. The deferred close releases the file on both the successful path and an early return.
Read into a buffer#
A stream read takes writable storage and returns the number of bytes filled:
let buffer = [u8]{ 0 x 4096 }
let count = file.read(buffer) ! return
let received = &buffer[0 .. count]
Use only the first count bytes after a read. A read may return fewer bytes than the buffer can hold; zero means end of input. Repeat reads when you need the rest of a stream.
Share code between files and other streams#
io.Reader and io.Writer describe reading and writing, rather than a particular storage medium. Files, memory buffers, and network connections can participate in these APIs.
io.copy(reader, writer) transfers data until the reader reaches its end. io.stdin(), io.stdout(), and io.stderr() expose the process's standard streams. ByteBuffer is a writer that accumulates bytes in memory, and text.reader() provides a reader over text.
When a write must be confirmed, check both the operation and the explicit close. A deferred close with _ is cleanup that ignores the close result.
Build a path#
Use fs.Path to join path components:
use valk.fs
fn main() {
let directory: fs.Path = "reports"
let file = directory.add("daily.txt")
println(file)
}
Path construction manipulates a path string; it does not create directories or open a file. Relative filesystem paths are interpreted from the process's working directory.