fs
Read, write and manage files and directories, and take paths apart and put them together.
import "std:fs"std:fs reads and writes files, lists and walks directories, copies and moves things, and builds paths. Most functions return a result, because files go missing and disks fill up. Pure path functions such as base and ext only work on strings and never touch the disk.
import "std:fs"Write and read text#
fs.writeFile(path, text) replaces a file's contents, creating the file if needed. fs.readFile(path) returns the whole file as a string. Both return results.
import "std:fs"
fs.writeFile("hello.txt", "Hello, file!\nSecond line\n").unwrap()
string text = fs.readFile("hello.txt").unwrap()
log text.len
log text.trim()Writing is atomic: another reader sees either the old contents or the new, never half a file, even if the program crashes mid-write. appendFile adds to the end instead of replacing:
import "std:fs"
fs.remove("log.txt", {force: true}).unwrap()
fs.appendFile("log.txt", "start\n").unwrap()
fs.appendFile("log.txt", "next\n").unwrap()
log fs.readFile("log.txt").unwrap().trim()Files that are not there#
A missing file is not a crash, it is an error value with a kind. Check for it and decide what to do.
import "std:fs"
switch fs.readFile("no-such-file.txt") (
case ok(text)
log "read" text.len
case err(e)
log e.kind
)
string config = fs.readFile("settings.txt").unwrapOr("default")
log configKinds you will meet are NotFoundError, PermissionError and IOError (everything else, such as reading a directory as a file). fs.exists(path) is true when something is there.
import "std:fs"
fs.mkdir("somedir", {recursive: true}).unwrap()
log fs.exists("somedir") fs.isDir("somedir") fs.isFile("somedir")
log fs.readFile("somedir").unwrapErr().kind
log fs.writeFile("no/such/dir/file.txt", "x").unwrapErr().kindWriting needs the parent directory to exist. Create it first with mkdir.
Try it: write settings.txt first and watch the fallback disappear.
Paths#
Paths are strings, and fs has functions to build them and take them apart, so you never join with "/" by hand. These work on the text only, and the file doesn't need to exist.
import "std:fs"
string p = fs.joinPath("projects", "osl", "main.osl")
log p
log fs.base(p) fs.dir(p)
log fs.ext(p) fs.stem(p)
log fs.splitPath(p) fs.splitExt(p)
log fs.segments("/usr/local/bin")joinPath accepts any number of parts and cleans the result. cleanPath resolves ., .. and doubled separators. Changing parts of a path is common enough to have helpers:
import "std:fs"
log fs.withExt("notes/todo.txt", "md")
log fs.withExt("notes/todo.txt", "")
log fs.withName("notes/todo.txt", "done.txt")
log fs.cleanPath("a//b/./c/../d")
log fs.joinPath("a", "../b", "c.txt")Relative and absolute paths:
import "std:fs"
log fs.isAbs("/etc/hosts") fs.isAbs("etc/hosts")
log fs.relPath("/a/b", "/a/b/c/d.txt")
log fs.relPath("/a/b", "/a/x")
log fs.pathStartsWith("/a/b/c", "/a/b") fs.pathStartsWith("/a/bc", "/a/b")
log fs.toPosix("a/b")
log fs.absPath("x.txt").unwrap().endsWith("/x.txt")pathStartsWith compares whole elements, so /a/bc is not inside /a/b. That is the check to use before acting on a path someone gave you, instead of comparing strings with startsWith.
Try it: call fs.ext and fs.stem on a name like archive.tar.gz.
Reading line by line#
Reading a whole file is fine for small ones. For logs and data files, loop over the lines. fs.lines returns an iterator inside a result, reads as it goes and closes the file when the loop ends.
import "std:fs"
fs.writeFile("names.txt", "ada\ngrace\n\nalan\n").unwrap()
for line in fs.lines("names.txt").unwrap() (
if line == "" continue
log line.toUpper()
)head and tail give the first or last lines of a file as an array, and tail reads backwards from the end, so it is cheap on a huge log. grep returns the lines that match a regular expression.
import "std:fs"
string[] rows = []
for i in 1 to 20 ( rows.append("row " ++ i) )
fs.writeFile("rows.txt", rows.join("\n") ++ "\n").unwrap()
log fs.head("rows.txt", 3).unwrap()
log fs.tail("rows.txt", 2).unwrap()
log fs.head("rows.txt").unwrap().len
log fs.grep("rows.txt", "^row 1.$").unwrap()
log fs.grep("rows.txt", "(").unwrapErr().kindBytes#
Text is not always text. Images, archives and binary formats are bytes. readFileBytes returns a byte[], and writeFileBytes accepts a byte[], a string (written as UTF-8) or an array of numbers from 0 to 255.
import "std:fs"
fs.writeFileBytes("data.bin", [0, 1, 2, 255]).unwrap()
byte[] data = fs.readFileBytes("data.bin").unwrap()
log data.len data[1] data[4]
fs.appendFileBytes("data.bin", [16, 32]).unwrap()
log fs.size("data.bin").unwrap()Information about files#
size, modTime and stat tell you about a file. stat returns a record with name, path, ext, size (bytes), modTime (unix milliseconds), mode (such as -rw-r--r--), isDir and isSymlink.
import "std:fs"
fs.writeFile("report.csv", "a,b\n1,2\n").unwrap()
auto info = fs.stat("report.csv").unwrap()
log info.name info.ext info.size info.isDir
log info.modTime > 0
log fs.size("report.csv").unwrap()
log fs.stat("nothing.here").unwrapErr().kindDirectories#
mkdir makes a directory. By default it fails if a parent is missing; with {recursive: true} it creates the whole chain and doesn't mind if it is already there. readDir lists names, and entries lists records (like stat) sorted by name.
import "std:fs"
fs.remove("proj", {force: true}).unwrap()
fs.mkdir("proj/src/util", {recursive: true}).unwrap()
fs.writeFile("proj/README.md", "# proj").unwrap()
fs.writeFile("proj/src/main.osl", "log 1").unwrap()
log fs.readDir("proj").unwrap()
for e in fs.entries("proj").unwrap() (
log e.name e.isDir
)
log fs.mkdir("proj").unwrapErr().kindWalking a tree#
fs.walkTree(path) visits everything below a directory, depth first and in name order, and the directory itself is not included. Each item is the same record that entries gives, so you can filter on ext or isDir as you go.
import "std:fs"
fs.remove("site", {force: true}).unwrap()
fs.mkdir("site/css", {recursive: true}).unwrap()
fs.mkdir("site/pages/deep", {recursive: true}).unwrap()
fs.writeFile("site/index.html", "<h1>hi</h1>").unwrap()
fs.writeFile("site/css/main.css", "body{}").unwrap()
fs.writeFile("site/pages/about.html", "about").unwrap()
fs.writeFile("site/pages/deep/team.html", "team").unwrap()
int bytes = 0
for e in fs.walkTree("site") (
if e.isDir continue
bytes += e.size
if e.ext == ".html" (
log fs.relPath("site", e.path)
)
)
log "bytes:" bytesGlob#
fs.glob matches a shell-style pattern: * and ? match within one path element, and [a-z] a range. It returns paths in name order, and no match is an empty array, not an error. ** is not special, so use walkTree to search all depths.
import "std:fs"
fs.remove("site", {force: true}).unwrap()
fs.mkdir("site/pages", {recursive: true}).unwrap()
fs.writeFile("site/index.html", "").unwrap()
fs.writeFile("site/logo.svg", "").unwrap()
fs.writeFile("site/pages/about.html", "").unwrap()
fs.writeFile("site/pages/blog.html", "").unwrap()
log fs.glob("site/*.html").unwrap()
log fs.glob("site/*/*.html").unwrap()
log fs.glob("site/pages/[a-b]*").unwrap()
log fs.glob("site/*.png").unwrap()
log fs.glob("[").unwrapErr().kindCopy, rename, remove#
copy copies a file, or a whole directory. It refuses to overwrite an existing destination file unless you pass {overwrite: true}. rename moves or renames, and replaces an existing file at the destination. remove deletes a file or a directory and everything in it.
import "std:fs"
fs.remove("work", {force: true}).unwrap()
fs.mkdir("work").unwrap()
fs.writeFile("work/a.txt", "A").unwrap()
fs.copy("work/a.txt", "work/b.txt").unwrap()
log fs.copy("work/a.txt", "work/b.txt").unwrapErr().kind
fs.copy("work/a.txt", "work/b.txt", {overwrite: true}).unwrap()
fs.rename("work/b.txt", "work/c.txt").unwrap()
log fs.readDir("work").unwrap()
fs.remove("backup", {force: true}).unwrap()
fs.copy("work", "backup").unwrap()
log fs.readDir("backup").unwrap()
fs.remove("work").unwrap()
log fs.exists("work")
log fs.remove("work").unwrapErr().kind
fs.remove("work", {force: true}).unwrap()remove on something that isn't there is an error, which is useful when you want to know. {force: true} makes it quietly succeed, which is what you want for cleanup. This is why the examples on this page start by removing what they create.
File handles#
For more control, fs.open returns a handle. Open it with mode "r" (read, the default), "w" (write from the start, creating or emptying the file) or "a" (append), and always close it. Writes are buffered until you flush or close.
import "std:fs"
auto out = fs.open("handle.txt", {mode: "w"}).unwrap()
out.write("first\n").unwrap()
out.write("second\n").unwrap()
out.write([116, 104, 105, 114, 100, 10]).unwrap()
out.close().unwrap()
auto f = fs.open("handle.txt").unwrap()
log f.readLine().unwrap()
log f.eof()
log f.read(3).unwrap()
log f.seek(0).unwrap()
log f.head(2).unwrap()
log f.tail(1).unwrap()
f.close().unwrap()readLine returns the next line, and reports an error at the end of the file, so loop with for line in f.lines() or check eof(). seek(offset, from) moves the cursor and takes "start", "current" or "end". Offsets are distances in bytes, so 0 is the start of the file.
fs.openText(text) gives you a handle over a string, so code that reads from a file handle can be tested without a file:
import "std:fs"
def countWords(*fs.File source) int (
int n = 0
for line in source.lines() ( n += line.split(" ").len )
return n
)
log countWords(fs.openText("one two\nthree four five"))Update with a backup#
Because writeFile is atomic, a program can update a settings file and keep the old one as a backup:
import "std:fs"
import "std:json"
fs.remove("settings.json", {force: true}).unwrap()
fs.remove("settings.json.bak", {force: true}).unwrap()
fs.writeFile("settings.json", json.stringify({volume: 5})).unwrap()
def update(string path, int volume) result<unit, error> (
fs.copy(path, path ++ ".bak", {overwrite: true})!
return fs.writeFile(path, json.stringify({volume: volume}))
)
update("settings.json", 9).unwrap()
log fs.readFile("settings.json").unwrap()
log fs.readFile("settings.json.bak").unwrap()
fs.remove("settings.json").unwrap()
fs.remove("settings.json.bak").unwrap()Count lines of code
Write a function countLines(dir) that returns how many lines there are in all .osl files below dir, skipping directories and other files. The starter creates a small project.
5
What does fs.remove("gone.txt") do when the file doesn't exist?
Rename .txt to .md
Rename every .txt file in notes to .md, then print the folder's names.
["a.md","b.md","keep.csv"]
API reference#
84 members, generated from the std:fs source of OSL 9.3.0.
Functions
fs.absPath(string path) result<string, error>absPath returns path as an absolute, cleaned path, resolved against the working directory. Fails with IOError when the working directory can't be read.
fs.append(any path) *fs.FileDeprecated. Deprecated: use fs.open(path, {mode: "a"}) instead.
append opens a file for appending, or returns null.
fs.appendFile(string path, string text) result<unit, error>appendFile adds text to the end of a file, creating it if needed. Fails with NotFoundError when the parent directory doesn't exist, PermissionError, or IOError.
fs.appendFileBytes(string path, any data) result<unit, error>appendFileBytes adds bytes to the end of a file, creating it if needed. data is read like fs.writeFileBytes data. Fails like fs.appendFile.
fs.appendToFile(any path, any data) booleanDeprecated. Deprecated: use fs.appendFile(path, text) instead.
appendToFile appends text and reports whether it worked.
fs.base(string path) stringbase returns the last element of a path: fs.base("a/b/c.txt") is "c.txt". It only reads the string.
fs.chdir(any path) booleanDeprecated. Deprecated: use process.chdir(path) instead.
chdir changes the working directory and reports whether it worked.
fs.cleanPath(string path) stringcleanPath returns the shortest equivalent path, resolving "." and ".." and repeated separators: fs.cleanPath("/a//b/./c") is "/a/b/c".
fs.copy(string from, string to, object? options) result<unit, error>copy copies a file, or a directory and everything in it, to a new path, keeping permissions. Options: overwrite (boolean, default false) replaces files that already exist at the destination; without it, an existing destination file fails with IOError. Fails with NotFoundError when from doesn't exist. Throws ValueError when copying a directory into itself.
fs.copyDir(any srcPath, any dstPath) booleanDeprecated. Deprecated: use fs.copy(from, to) instead, which copies files and directories.
copyDir copies a directory tree into dstPath and reports whether it worked.
fs.create(any path) *fs.FileDeprecated. Deprecated: use fs.open(path, {mode: "w"}) instead.
create opens a file for writing, truncating it, or returns null.
fs.dir(string path) stringdir returns everything but the last element of a path: fs.dir("a/b/c.txt") is "a/b". It only reads the string.
fs.eachLine(any path, any fn)Deprecated. Deprecated: use for line in fs.lines(path) instead.
eachLine calls fn with every line of a file; a missing file calls it never.
fs.entries(string path) result<{ext: string, isDir: boolean, isSymlink: boolean, modTime: int, mode: string, name: string, path: string, size: int}[], error>entries lists a directory's entries, sorted by name. Each entry has the keys name, path (path joined with name), ext, isDir, isSymlink, size (bytes), modTime (unix milliseconds) and mode (such as "-rw-r--r--"). Fails like fs.readDir.
fs.evalSymlinks(string path) result<string, error>evalSymlinks resolves every symlink in a path. Fails with NotFoundError when the path or a link target doesn't exist.
fs.exists(string path) booleanexists reports whether something exists at path. A broken symlink counts as missing.
fs.ext(string path) stringext returns the extension of a path's last element, with its dot: fs.ext("a/b/c.txt") is ".txt", and "" when there is none.
fs.getBase(any path) stringDeprecated. Deprecated: use fs.base(path) instead.
getBase returns the last element of a path.
fs.getDir(any path) stringDeprecated. Deprecated: use fs.dir(path) instead.
getDir returns a path's directory.
fs.getExt(any path) stringDeprecated. Deprecated: use fs.ext(path) instead.
getExt returns a path's extension.
fs.getModTime(any path) numberDeprecated. Deprecated: use fs.modTime(path) instead.
getModTime returns a file's modification time in unix milliseconds, or 0 when it can't be read.
fs.getParts(any path) string[]Deprecated. Deprecated: use fs.base(path), fs.dir(path) and fs.ext(path) instead.
getParts returns [base, dir, ext] of a path.
fs.getSize(any path) numberDeprecated. Deprecated: use fs.size(path) instead.
getSize returns a file's size in bytes, or 0 when it can't be read.
fs.getStat(any path) objectDeprecated. Deprecated: use fs.stat(path) instead.
getStat describes a file, with success false when it can't be read.
fs.getStem(any path) stringDeprecated. Deprecated: use fs.stem(path) instead.
getStem returns a path's last element without its extension.
fs.getwd() stringDeprecated. Deprecated: use process.cwd() instead.
getwd returns the working directory, or "" when it can't be read.
fs.glob(string pattern) result<string[], error>glob lists the paths matching a shell pattern such as "src/.osl", in lexical order. and ? match within one path element and [a-z] matches a range; ** is not special. Fails with ParseError for a malformed pattern.
fs.grep(string path, string pattern) result<string[], error>grep returns the lines of a file that match a regular expression. Fails with NotFoundError when the file doesn't exist and ParseError for an invalid pattern.
fs.head(string path, any ...count) result<string[], error>head returns up to the first count lines (default 10) of a file. Fails with NotFoundError when the file doesn't exist; throws ValueError for a negative count.
fs.isAbs(string path) booleanisAbs reports whether a path is absolute.
fs.isDir(string path) booleanisDir reports whether path is a directory, following symlinks.
fs.isFile(string path) booleanisFile reports whether path is a regular file, following symlinks.
fs.joinPath(any ...path) stringjoinPath joins path elements with the OS separator and cleans the result: fs.joinPath("a", "b", "c.txt") is "a/b/c.txt".
fs.lines(string path) result<iter<string>, error>lines iterates over a file's lines, without line endings, reading as it goes; the file closes when the loop finishes. Fails with NotFoundError when the file doesn't exist; a read error throws IOError from the loop.
fs.mkdir(string path, object? options) result<unit, error>mkdir creates a directory. Options: recursive (boolean, default false) also creates missing parents and succeeds when the directory already exists. Fails with NotFoundError when a parent is missing (without recursive), IOError when path already exists, or PermissionError.
fs.mkdirAll(any path) booleanDeprecated. Deprecated: use fs.mkdir(path, {recursive: true}) instead.
mkdirAll creates a directory and any missing parents and reports whether it worked.
fs.modTime(string path) result<int, error>modTime returns when a file was last modified, in unix milliseconds, following symlinks. Fails with NotFoundError when the file doesn't exist.
fs.open(string path, object? options) result<*fs.File, error>open opens a file and returns a handle; close it when done. Options: mode is "r" (default) to read, "w" to write from the start, creating or truncating the file, or "a" to append, creating it if needed. Fails with NotFoundError when a file opened for reading (or the parent directory) doesn't exist, PermissionError, or IOError. Throws ValueError for an unknown mode.
fs.openText(string text) *fs.FileopenText returns a readable handle over text, for reading it the way a file is read.
fs.pathStartsWith(string path, string prefix) booleanpathStartsWith reports whether path is prefix or lies below it, comparing whole elements: "/a/bc" does not start with "/a/b".
fs.readDir(string path) result<string[], error>readDir lists the names of the entries in a directory, sorted by name. Fails with NotFoundError when the directory doesn't exist, PermissionError, or IOError when path isn't a directory.
fs.readDirAll(any path) {ext: string, isDir: boolean, name: string, path: string, type: any}[]Deprecated. Deprecated: use fs.entries(path) instead.
readDirAll lists a directory's entries, or [] when it can't be read.
fs.readFile(string path) result<string, error>readFile reads a whole file as text. Fails with NotFoundError when the file doesn't exist, PermissionError when it can't be read, and IOError for anything else, such as path being a directory.
fs.readFileBytes(string path) result<byte[], error>readFileBytes reads a whole file as bytes. Fails like fs.readFile.
fs.relPath(string base, string target) stringrelPath returns target relative to base, with forward slashes: fs.relPath("/a/b", "/a/b/c") is "c". Throws ValueError when one path is absolute and the other isn't.
fs.remove(string path, object? options) result<unit, error>remove deletes a file, or a directory and everything in it. A symlink is removed, not its target. Fails with NotFoundError when path doesn't exist, so used as a boolean it is true only when something was removed, and PermissionError or IOError when something can't be deleted. Option: force (boolean, default false) succeeds without doing anything when path doesn't exist, like rm -rf, for cleanup of a path that may already be gone.
fs.rename(string from, string to) result<unit, error>rename moves or renames a file or directory, replacing an existing file at to. Fails with NotFoundError when from doesn't exist, PermissionError, or IOError (for example when to is on another device).
fs.segments(string path) string[]segments lists a cleaned path's elements without separators: fs.segments("/usr/local/bin") is ["usr", "local", "bin"].
fs.size(string path) result<int, error>size returns a file's size in bytes, following symlinks. Fails with NotFoundError when the file doesn't exist.
fs.splitExt(string path) string[]splitExt splits a path into [path without extension, extension]: fs.splitExt("a/b.txt") is ["a/b", ".txt"].
fs.splitPath(string path) string[]splitPath splits a path after its last separator into [directory, name]: fs.splitPath("a/b/c.txt") is ["a/b/", "c.txt"].
fs.stat(string path) result<{ext: string, isDir: boolean, isSymlink: boolean, modTime: int, mode: string, name: string, path: string, size: int}, error>stat describes the file, directory or symlink at path, with the same keys as an fs.entries entry. A symlink describes its target, with isSymlink true. Fails with NotFoundError when nothing exists at path.
fs.stem(string path) stringstem returns the last element of a path without its extension: fs.stem("a/b/c.txt") is "c".
fs.syncFile(string path) result<unit, error>syncFile flushes a file's written data to disk. Fails with NotFoundError when the file doesn't exist; it never creates one.
fs.syncLater(string path) result<unit, error>syncLater queues a file to be flushed to disk in the background, merging repeated requests for the same file; fs.syncPending waits for the queue. A file removed before its flush is skipped. Throws ValueError for an empty path.
fs.syncPending() result<unit, error>syncPending waits until every file queued with fs.syncLater before the call is flushed. Fails with the first flush error, usually an IOError, including one from a flush that finished before the call. Every call waiting on a flush reports its failure, and a failure no call was waiting on is reported once, by the next call.
fs.tail(string path, any ...count) result<string[], error>tail returns up to the last count lines (default 10) of a file, reading backwards from the end. Fails with NotFoundError when the file doesn't exist; throws ValueError for a negative count.
fs.toPosix(string path) stringtoPosix converts the OS separators in a path to forward slashes.
fs.tryAppendToFile(any path, any data) resultDeprecated. Deprecated: use fs.appendFile(path, text) instead, which returns a result.
tryAppendToFile appends to a file into a result with a string error.
fs.tryMkdirAll(any path) resultDeprecated. Deprecated: use fs.mkdir(path, {recursive: true}) instead, which returns a result.
tryMkdirAll creates directories into a result with a string error.
fs.tryReadDir(any path) resultDeprecated. Deprecated: use fs.readDir(path) instead, which returns a result.
tryReadDir lists names into a result with a string error.
fs.tryReadFile(any path) resultDeprecated. Deprecated: use fs.readFile(path) instead, which returns a result.
tryReadFile reads a file into a result with a string error.
fs.tryRemove(any path) resultDeprecated. Deprecated: use fs.remove(path, {force: true}) instead, which returns a result.
tryRemove removes into a result with a string error.
fs.tryRename(any oldPath, any newPath) resultDeprecated. Deprecated: use fs.rename(from, to) instead, which returns a result.
tryRename renames into a result with a string error.
fs.tryWriteFile(any path, any data) resultDeprecated. Deprecated: use fs.writeFile(path, text) instead, which returns a result.
tryWriteFile writes a file into a result with a string error.
fs.walk(any path) {ext: string, isDir: boolean, name: string, path: string, type: any}[]Deprecated. Deprecated: use for entry in fs.walkTree(path) instead.
walk lists everything below path, or [] when it can't be read.
fs.walkDir(any path, any fn)Deprecated. Deprecated: use for entry in fs.walkTree(path) instead.
walkDir calls fn(path, file, control) for every entry under path, including path itself; setting control.skip skips a directory.
fs.walkTree(string path) iter<{ext: string, isDir: boolean, isSymlink: boolean, modTime: int, mode: string, name: string, path: string, size: int}>walkTree visits everything below a directory, depth first in lexical order, without following symlinks; the root itself is not included. It yields entries shaped like fs.entries ones. A missing or unreadable root or subdirectory throws NotFoundError, PermissionError or IOError from the loop that is iterating.
fs.withExt(string path, string ext) stringwithExt replaces a path's extension; the dot is optional and "" removes it: fs.withExt("a/b.txt", "md") is "a/b.md".
fs.withName(string path, string name) stringwithName replaces a path's last element: fs.withName("a/b/c.txt", "d.md") is "a/b/d.md".
fs.writeFile(string path, string text, object? options) result<unit, error>writeFile replaces a file's contents with text, creating it if needed. The write is atomic: readers see the old or the new contents, never a mix, even after a crash, and an existing file keeps its permissions. Writing through a symlink replaces its target. Fails with NotFoundError when the parent directory doesn't exist, PermissionError, or IOError. Options: durable (boolean, default true) returns only once the new contents are on stable storage; false keeps the write atomic but may return first, so a crash right after can leave the old contents. On macOS false uses a write barrier, and on Linux with ext4, XFS or btrfs it waits for the data to reach the disk without flushing its cache; elsewhere it flushes in full, like a durable write.
fs.writeFileBytes(string path, any data, object? options) result<unit, error>writeFileBytes replaces a file's contents with bytes, atomically like fs.writeFile and with the same options. data is a byte[]; a string writes its UTF-8 bytes and an array of whole numbers from 0 to 255 writes those bytes. Throws ValueError for a number outside that range.
*fs.File
file.close() result<unit, error>close flushes buffered writes and closes the file. Closing again does nothing. Fails with IOError when the final flush fails.
file.eof() booleaneof reports whether there is nothing left to read. A closed or write-only handle has nothing to read.
file.flush() result<unit, error>flush writes buffered data to the file. Throws StateError on a closed or read-only handle; fails with IOError.
file.grep(string pattern) result<string[], error>grep reads the remaining lines and returns those matching a regular expression. Fails with ParseError for an invalid pattern and IOError when reading fails. Throws StateError on a closed or write-only handle.
file.head(any ...count) result<string[], error>head reads up to count lines (default 10) from the current position. Throws StateError on a closed or write-only handle and ValueError for a negative count; fails with IOError when reading fails.
file.lines() iter<string>lines iterates over the remaining lines, without line endings. A read error throws IOError from the loop. Throws StateError on a closed or write-only handle.
file.read(any ...count) result<string, error>read reads text from the current position: everything that is left, or at most count bytes when count is given. At the end of the file it returns "". Throws StateError on a closed or write-only handle and ValueError for a negative count; fails with IOError when reading fails.
file.readBytes(any ...count) result<byte[], error>readBytes reads bytes from the current position: everything that is left, or at most count bytes when count is given. Fails like file.read.
file.readLine() result<string, error>readLine reads the next line, without its line ending ("\n" or "\r\n"). Fails with IOError at the end of the file, so check file.eof() first or loop with for line in file.lines(). Throws StateError on a closed or write-only handle.
file.seek(int offset, any ...from) result<int, error>seek moves the read and write cursor offset bytes from "start" (the default), "current" or "end", and returns its new offset from the start: the number of bytes before it, so 0 is the start of the file. Offsets are distances, not 1-based positions. Buffered writes are flushed first. Throws StateError on a closed handle and ValueError for another origin; fails with IOError.
file.tail(any ...count) result<string[], error>tail reads the last count lines (default 10) of the whole file, without moving the read position. Throws StateError on a closed or write-only handle and ValueError for a negative count; fails with IOError.
file.write(any data) result<unit, error>write writes data at the current position: a string, a byte[] or an array of whole numbers from 0 to 255; other values are written as their string form. Writes are buffered until file.flush() or file.close(). Throws StateError on a closed or read-only handle; fails with IOError.