Antra

Standardbibliothek

Antra::IO

import Antra::IO

File I/O for Antra.

slurp and spit intentionally return Result values instead of throwing directly. They stay as the path-based convenience layer on top of Antra::IO::File.

Überblick#

Neben slurp(...) und spit(...) gibt es in Antra::IO jetzt auch kleine Helper fuer die drei Standardstreams des aktuellen Prozesses:

  • IO.stdin()
  • IO.stdout()
  • IO.stderr()
  • IO.pipe()

Sie liefern normale FileHandle-Werte und koennen deshalb direkt mit print(...), println(...), File.read(...) oder File.write(...) verwendet werden.

import Antra::IO as IO

my out = IO.stdout()
println("hello", [to: out])

Die Rueckgabe sind dabei bewusst duplizierte Handles. Wenn du so einen Handle mit File.close(...) schliesst, bleibt der globale Prozess-Stream selbst trotzdem benutzbar.

IO.pipe() erzeugt dagegen ein frisches anonymes Reader/Writer-Paar fuer niedrigstufige Stream-Verkabelung:

import Antra::IO as IO
import Antra::IO::File as File
import Antra::Result as Result

my [reader, writer] = IO.pipe().!
Result.unwrap!(File.write(writer, "hello"))
Result.unwrap!(File.close(writer))
println(File.read_all(reader).!)
Result.unwrap!(File.close(reader))

Referenz#

IO.slurp(path)
IO.slurp(path, [encoding: "UTF-8"])
IO.spit(path, content)
IO.spit(path, content, [append: true])
IO.spit(path, content, [append: true, encoding: "UTF-8"])
IO.prompt(prompt)
IO.prompt(prompt).!
IO.prompt_required(prompt)
IO.prompt_required(prompt).!
IO.pipe()
IO.pipe().!

Fuer handle-basiertes Lesen und Schreiben gibt es zusaetzlich Antra::IO::File:

File.open(path)
File.open(path, [mode: [:read], binary: false, encoding: "UTF-8"])
File.open(path).!
File.read(handle, n)
File.read_all(handle)
File.read_byte(handle)
File.read_byte(handle).!
File.read_char(handle)
File.read_char(handle).!
File.read_line(handle)
File.read_line(handle).!
File.write(handle, data)
File.flush(handle)
File.seek(handle, offset)
File.seek(handle, offset, [from: :start])
File.position(handle)
File.close(handle)
File.with_open(path, |fh| -> ...)
File.with_open(path, [binary: true], |fh| -> ...)
File.with_open!(path, |fh| -> ...)

open, with_open und with_open! nennen ihre Optionen in der Signatur statt ein undurchsichtiges Array zu nehmen. Drei gibt es: mode: (Flags :read, :write, :append, :create, :truncate als Array oder einzeln, dazu die Kurz-Aliase "<", ">", ">>", "<+", "+>"), binary: und encoding:. Das Array darf ganz entfallen — auch wenn ein Callback dahinter steht. Ein falsch geschriebener Name ist deshalb kein still ignorierter Key mehr, sondern ein Fehler, der die erlaubten nennt:

Error: No matching clause for File.open with 2 args: notes.txt, [binry: true]
Hint: Unknown key(s) in keyword-array argument 2: binry. Accepted: mode, binary, encoding.

File.read_line(...) liest genau eine Textzeile vom aktuellen Handle-Offset, entfernt ein abschliessendes \n oder \r\n und liefert %Ok[%Some[line]] fuer gelesene Zeilen sowie %Ok[%None[]] an EOF statt eines Fehlers.

File.read_byte(...) ist die binaere Schwester dazu: genau ein Byte lesen, %Ok[%Some[0..255]] bei Erfolg und %Ok[%None[]] an EOF. Umgekehrt liest File.read_char(...) genau ein decodiertes Zeichen von einem Text-Handle und liefert ebenfalls %Ok[%Some[...]] oder %Ok[%None[]].

Diese drei sind an einen Modus gebunden, und der falsche ist ein Fehler, kein stiller Fallback:

FunktionText-Handlebinary: true
read / read_all%Ok[String]%Ok[ByteString]
read_line%Ok[%Some[line]]Fehler
read_char%Ok[%Some[char]]Fehler
read_byteFehler%Ok[%Some[0..255]]

read_line und read_char dekodieren nach encoding: — im Binaermodus gibt es nichts zu dekodieren, und was eine „Zeile" aus Bytes waere (Trennzeichen? \r mitzaehlen?) beantwortet die Funktion bewusst nicht selbst. Wer Bytes will, nimmt read/read_all oder read_byte.

Fuer einfache interaktive CLI-Eingabe gibt es zusaetzlich IO.prompt(...). Die Funktion schreibt den Prompt ohne automatischen Zeilenumbruch nach stdout und liest danach genau eine Zeile von stdin als %Ok[%Some[line]] | %Ok[%None[]] | %Err[%Error{...}].

Wenn fehlende Eingabe fachlich nicht normal, sondern exceptional ist, gibt es zusaetzlich IO.prompt_required(...). Diese Schwester hebt EOF in einen %Err[%Error{...}]-Fall an und passt damit besser zu linearen use-Flows.

Funktionen#

fn pipe()

Creates an anonymous pipe as [reader, writer].

The returned handles are normal FileHandle values and can be used with Antra::IO::File or passed into fork/process helpers.

Example:

Result.run {
    val [reader, writer] = IO.pipe().^
    File.write(writer, "hello").^
    File.close(writer).^
    println(File.read_all(reader).^)
    File.close(reader).^
}
fn prompt(prompt_text)

Writes prompt to stdout and reads one line from stdin.

Returns %Ok[%Some[line]], %Ok[%None[]] at EOF, or %Err[%Error{...}].

fn prompt_required(prompt_text)

Writes prompt to stdout and requires one input line from stdin.

Returns %Ok[line] on success or %Err[%Error{...}] on EOF or I/O error.

fn read_key()

Reads one logical key from stdin.

Returns %Ok[%Some[key]], %Ok[%None[]] at EOF, or %Err[%Error{...}].

fn read_line_cancelable()

Reads one line from stdin, turning a Ctrl-C (SIGINT) during the read into a cancellation instead of killing the process.

Line editing works as usual (canonical mode). Returns %Ok[%Some[line]], %Ok[%None[]] at EOF, or %Err[%Error{kind: :cancelled}] on Ctrl-C. On a non-TTY stdin it degrades to a normal line read.

fn read_line_hidden()

Reads one line from stdin with terminal echo disabled (password input).

Line editing and Enter work as usual; only the echo is suppressed. On a non-TTY stdin it degrades to a normal line read. Returns %Ok[%Some[line]], %Ok[%None[]] at EOF, or %Err[%Error{...}].

fn read_line_masked(mask)

Reads one line from stdin echoing mask (e.g. "*") for each character instead of the real input, with visible Backspace/Delete editing.

Line editing works on a per-character basis; only the mask is shown. On a non-TTY stdin it degrades to a normal line read. Returns %Ok[%Some[line]], %Ok[%None[]] at EOF, or %Err[%Error{kind: :cancelled}] on Ctrl-C.

fn select(opts)

Waits until at least one of the given handles is ready to be read.

Accepts FileHandle and Socket values in one call — pipes, standard streams, open files, connected sockets and listeners. Returns readiness only, never data: use File.read(...) or Socket.recv(...) afterwards. EOF counts as readable, which is how a closed peer is detected.

timeout_ms: is undef to block until a handle is ready, 0 for an immediate snapshot, or a positive number of milliseconds. A timeout without an event is not an error and yields %{read: []}.

Returns %Ok[%{read: [handle, ...]}] in input order, or %Err[%Error{...}].

Example:

my ready = IO.select([read: [stdout, stderr], timeout_ms: 100]).^
if (Enum.member?(ready.read, stdout)) {
    println(File.read(stdout, 1024).^)
}
fn select!(opts)
fn slurp(path)
fn slurp(path, [encoding: = "UTF-8", ..])

Reads a file as a string.

Returns %Ok["..."] or %Err[%Error{...}].

Example:

val raw = IO.slurp("examples/people.csv").^
val legacy = IO.slurp("legacy.txt", [encoding: "ISO-8859-1"]).^
fn spit(path, content)
fn spit(path, content, [append: = false, encoding: = "UTF-8", ..])

Writes a string to a file.

Returns %Ok[undef] or %Err[%Error{...}].

Example:

IO.spit("out.txt", "hello").^
IO.spit("out.txt", " world", [append: true]).^
IO.spit("legacy.txt", "hallo", [encoding: "ISO-8859-1"]).^
fn stderr()

Returns a writable handle for the current process STDERR.

fn stdin()

Returns a readable handle for the current process STDIN.

fn stdin_is_tty()

Returns true when the current stdin is attached to a TTY.

fn stdout()

Returns a writable handle for the current process STDOUT.

fn with_raw_stdin(f)

Enables raw stdin for the dynamic scope of f and restores the previous terminal state afterwards.

Quelle dieser Seite