io - File operations #

The io module provides file and stream input/output operations.

File Reading #

io.read(path) #

Read entire file contents as a string

Parameters:

  • path - File path (Str)

Returns: File contents (Str)

Raises: Error if file doesn't exist or can't be read

Example:

let content = io.read("data.txt")
puts(content)

io.read_lines(path) #

Read file as list of lines (with newlines stripped)

Parameters:

  • path - File path (Str)

Returns: List of lines (List of Str)

Example:

let lines = io.read_lines("input.txt")
for line in lines
    puts(line)
end

io.read_bytes(path) #

Read file as raw bytes

Parameters:

  • path - File path (Str)

Returns: Byte array (Bytes)

Example:

let data = io.read_bytes("image.png")

File Writing #

io.write(path, content) #

Write string to file (overwrites existing content)

Parameters:

  • path - File path (Str)
  • content - Content to write (Str)

Returns: Nil

Example:

io.write("output.txt", "Hello, World!")

io.write_lines(path, lines) #

Write list of strings to file (adds newlines)

Parameters:

  • path - File path (Str)
  • lines - List of strings (List)

Returns: Nil

Example:

let lines = ["First line", "Second line", "Third line"]
io.write_lines("output.txt", lines)

io.write_bytes(path, bytes) #

Write raw bytes to file

Parameters:

  • path - File path (Str)
  • bytes - Byte data (Bytes)

Returns: Nil

io.append(path, content) #

Append string to file (creates if doesn't exist)

Parameters:

  • path - File path (Str)
  • content - Content to append (Str)

Returns: Nil

Example:

io.append("log.txt", "New log entry\n")

File Operations #

io.exists(path) #

Check if file or directory exists

Parameters:

  • path - File or directory path (Str)

Returns: Bool (true if exists)

Example:

if io.exists("config.json")
    let config = io.read("config.json")
end

io.is_file(path) #

Check if path is a file

Parameters:

  • path - Path (Str)

Returns: Bool

io.is_dir(path) #

Check if path is a directory

Parameters:

  • path - Path (Str)

Returns: Bool

io.size(path) #

Get file size in bytes

Parameters:

  • path - File path (Str)

Returns: Size in bytes (Num)

Example:

let size = io.size("large_file.dat")
puts("File is ", size, " bytes")

io.copy(src, dst) #

Copy file from source to destination

Parameters:

  • src - Source file path (Str)
  • dst - Destination file path (Str)

Returns: Nil

Example:

io.copy("original.txt", "backup.txt")

io.move(src, dst) #

Move/rename file

Parameters:

  • src - Source file path (Str)
  • dst - Destination file path (Str)

Returns: Nil

io.remove(path) #

Delete file

Parameters:

  • path - File path (Str)

Returns: Nil

Example:

if io.exists("temp.txt")
    io.remove("temp.txt")
end

Directory Operations #

io.mkdir(path) #

Create directory (fails if parent doesn't exist)

Parameters:

  • path - Directory path (Str)

Returns: Nil

io.mkdir_all(path) #

Create directory and all parent directories

Parameters:

  • path - Directory path (Str)

Returns: Nil

Example:

io.mkdir_all("data/outputs/2024")

io.rmdir(path) #

Remove empty directory

Parameters:

  • path - Directory path (Str)

Returns: Nil

io.rmdir_all(path) #

Remove directory and all contents recursively

Parameters:

  • path - Directory path (Str)

Returns: Nil

Warning: Use with caution - permanently deletes all contents

io.list_dir(path) #

List files and directories in path

Parameters:

  • path - Directory path (Str)

Returns: List of names (List of Str)

Example:

let files = io.list_dir(".")
for file in files
    puts(file)
end

io.walk(path) #

Recursively walk directory tree

Parameters:

  • path - Root directory path (Str)

Returns: List of all file paths (List of Str)

Example:

let all_files = io.walk("src")
for file in all_files
    if file.endswith(".q")
        puts(file)
    end
end

File Metadata #

io.modified_time(path) #

Get last modification time as Unix timestamp

Parameters:

  • path - File path (Str)

Returns: Unix timestamp (Num)

io.created_time(path) #

Get creation time as Unix timestamp

Parameters:

  • path - File path (Str)

Returns: Unix timestamp (Num)

io.accessed_time(path) #

Get last access time as Unix timestamp

Parameters:

  • path - File path (Str)

Returns: Unix timestamp (Num)

Path Utilities #

io.absolute(path) #

Convert relative path to absolute path

Parameters:

  • path - Relative or absolute path (Str)

Returns: Absolute path (Str)

Example:

let abs = io.absolute("../data/file.txt")
puts(abs)  # /Users/name/project/data/file.txt

io.basename(path) #

Get filename from path

Parameters:

  • path - File path (Str)

Returns: Filename (Str)

Example:

io.basename("/path/to/file.txt")  # Returns "file.txt"

io.dirname(path) #

Get directory from path

Parameters:

  • path - File path (Str)

Returns: Directory path (Str)

Example:

io.dirname("/path/to/file.txt")  # Returns "/path/to"

io.extension(path) #

Get file extension

Parameters:

  • path - File path (Str)

Returns: Extension including dot (Str) or empty string

Example:

io.extension("file.txt")  # Returns ".txt"
io.extension("archive.tar.gz")  # Returns ".gz"

io.join(parts...) #

Join path components with appropriate separator

Parameters:

  • parts... - Path components (Str)

Returns: Joined path (Str)

Example:

let path = io.join("data", "outputs", "results.csv")
# Returns "data/outputs/results.csv" on Unix
# Returns "data\outputs\results.csv" on Windows

Glob/Pattern Matching #

io.glob(pattern) #

Find all files matching a glob pattern

Parameters:

  • pattern - Glob pattern (Str)
    • * matches any characters except /
    • ** matches any characters including / (recursive)
    • ? matches single character
    • [abc] matches one character from set
    • [!abc] matches one character not in set

Returns: List of matching file paths (List of Str)

Example:

# Find all .q files in current directory
let quest_files = io.glob("*.q")

# Find all .txt files recursively
let all_text = io.glob("**/*.txt")

# Find all test files
let tests = io.glob("test_*.q")

# Multiple patterns
let sources = io.glob("src/**/*.{q,md}")

io.glob_match(path, pattern) #

Check if a path matches a glob pattern

Parameters:

  • path - File path to test (Str)
  • pattern - Glob pattern (Str)

Returns: Bool (true if matches)

Example:

if io.glob_match("test_utils.q", "test_*.q")
    puts("This is a test file")
end

StringIO - In-Memory String Buffers #

StringIO provides an in-memory string buffer with a file-like interface. It's useful for testing, building strings efficiently, and capturing output without creating temporary files.

io.StringIO.new() #

Create empty in-memory string buffer

Returns: StringIO object

Example:

let buf = io.StringIO.new()
buf.write("Hello")
buf.write(" World")
puts(buf.get_value())  # "Hello World"

io.StringIO.new(initial) #

Create StringIO with initial content

Parameters:

  • initial - Initial buffer content (Str)

Returns: StringIO object

Example:

let buf = io.StringIO.new("Initial text")
buf.write(" more text")
puts(buf.get_value())  # "Initial text more text"

StringIO Methods #

Writing Methods #

write(data) → Int Write string to buffer (always appends to end). Returns number of bytes written.

let buf = io.StringIO.new()
let count = buf.write("Hello")  # Returns 5

writelines(lines) → Nil Write array of strings to buffer (does not add newlines automatically).

let buf = io.StringIO.new()
buf.writelines(["Line 1\n", "Line 2\n"])

Reading Methods #

get_value() → Str / getvalue() → Str Get entire buffer contents (regardless of current position). Both spellings supported for Python compatibility.

let buf = io.StringIO.new("Hello World")
puts(buf.get_value())  # "Hello World"

read() → Str Read from current position to end of buffer.

let buf = io.StringIO.new("Hello World")
buf.seek(6)
puts(buf.read())  # "World"

read(size) → Str Read up to size characters from current position.

let buf = io.StringIO.new("Hello World")
puts(buf.read(5))  # "Hello"

readline() → Str Read one line (up to and including newline).

let buf = io.StringIO.new("Line 1\nLine 2\n")
puts(buf.readline())  # "Line 1\n"
puts(buf.readline())  # "Line 2\n"

readlines() → Array[Str] Read all lines from current position as array.

let buf = io.StringIO.new("A\nB\nC")
let lines = buf.readlines()  # ["A\n", "B\n", "C"]

Position Methods #

tell() → Int Get current position in buffer (0-based byte offset).

seek(pos) → Int Seek to absolute position. Returns new position.

let buf = io.StringIO.new("Hello World")
buf.seek(6)
puts(buf.read())  # "World"

seek(offset, whence) → Int Seek relative to reference point. Returns new position.

Whence values:

  • 0 - SEEK_SET: Seek from beginning
  • 1 - SEEK_CUR: Seek from current position
  • 2 - SEEK_END: Seek from end
let buf = io.StringIO.new("Hello World")
buf.seek(-5, 2)   # 5 bytes before end
puts(buf.read())  # "World"

Utility Methods #

clear() → Nil Clear buffer contents and reset position to 0.

truncate() → Int / truncate(size) → Int Truncate buffer to size (defaults to current position). Returns new size.

let buf = io.StringIO.new("Hello World")
buf.seek(5)
buf.truncate()  # Truncate at position 5
puts(buf.get_value())  # "Hello"

len() → Int Get buffer length in bytes.

char_len() → Int Get buffer length in Unicode characters (useful for UTF-8).

let buf = io.StringIO.new("Hello → World")
puts(buf.len())       # 15 (bytes)
puts(buf.char_len())  # 13 (characters)

empty() → Bool Check if buffer is empty.

flush() → Nil No-op (included for file compatibility).

close() → Nil No-op (included for file compatibility).

closed() → Bool Always returns false (StringIO never closes).

Context Manager Support #

StringIO supports the with statement for automatic cleanup:

with io.StringIO.new() as buf
    buf.write("Line 1\n")
    buf.write("Line 2\n")
    puts(buf.get_value())
end

StringIO Examples #

Example 1: Building strings efficiently

use "std/io"

fun build_report(items)
    let buf = io.StringIO.new()

    buf.write("Report\n")
    buf.write("======\n\n")

    for item in items
        buf.write("- ")
        buf.write(item.name)
        buf.write(": ")
        buf.write(item.value.to_string())
        buf.write("\n")
    end

    buf.get_value()
end

Example 2: Parsing line-by-line

let data = io.StringIO.new("Name: Alice\nAge: 30\nCity: NYC")
let user = {}

while true
    let line = data.readline()
    if line == ""
        break
    end

    let parts = line.trim().split(":")
    user[parts[0]] = parts[1].trim()
end

Example 3: Testing output

use "std/test"

test.it("generates correct output", fun ()
    let buf = io.StringIO.new()

    # Capture output by writing to buffer
    buf.write("Header\n")
    buf.write("Data\n")

    let result = buf.get_value()
    test.assert(result.contains("Header"), nil)
    test.assert(result.contains("Data"), nil)
end)

When to use StringIO vs string concatenation:

  • Use StringIO for: Building strings in loops (>10 iterations), line-by-line processing, capturing output, testing
  • Use string concat (..) for: Simple 2-3 concatenations, inline string construction

Stream/Handle Operations #

io.open(path, mode = "r") #

Open file and return file handle

Parameters:

  • path - File path (Str)
  • mode - Open mode (Str): "r" (read), "w" (write), "a" (append), "r+" (read/write)

Returns: File handle (File)

Example:

let f = io.open("data.txt", "r")
let content = f.read()
f.close()

File Handle Methods #

file.read() #

Read entire file contents

Returns: File contents (Str)

file.read_line() #

Read single line (returns nil at EOF)

Returns: Line string or Nil

file.write(content) #

Write string to file

Parameters:

  • content - String to write (Str)

file.flush() #

Flush write buffer to disk

file.close() #

Close file handle

file.seek(position) #

Seek to position in file

Parameters:

  • position - Byte offset (Num)

file.tell() #

Get current position in file

Returns: Byte offset (Num)

io.with_file(path, mode, fn) #

Open file, execute function, automatically close

Parameters:

  • path - File path (Str)
  • mode - Open mode (Str)
  • fn - Function to execute with file handle

Returns: Result of function

Example:

io.with_file("output.txt", "w", fun(f)
    f.write("Line 1\n")
    f.write("Line 2\n")
end)

Standard Streams #

io.stdin #

Standard input stream (File handle)

io.stdout #

Standard output stream (File handle)

io.stderr #

Standard error stream (File handle)

Example:

io.stderr.write("Error: something went wrong\n")

io.read_line() #

Read line from standard input

Returns: Input line (Str)

Example:

print("Enter your name: ")
let name = io.read_line()
puts("Hello, ", name, "!")

Temporary Files #

io.temp_file(prefix = "quest") #

Create temporary file

Parameters:

  • prefix - Filename prefix (Str)

Returns: File handle to temp file (File)

io.temp_dir(prefix = "quest") #

Create temporary directory

Parameters:

  • prefix - Directory name prefix (Str)

Returns: Path to temp directory (Str)

Common Patterns #

Reading and Processing Lines #

let lines = io.read_lines("input.txt")
let results = []

for line in lines
    if !line.startswith("#")  # Skip comments
        results.append(line.trim())
    end
end

io.write_lines("output.txt", results)

Safe File Writing #

io.with_file("config.json", "w", fun(f)
    f.write("{")
    f.write("  \"version\": 1,")
    f.write("  \"enabled\": true")
    f.write("}")
end)

Directory Traversal #

let quest_files = []
for file in io.walk("src")
    if io.extension(file) == ".q"
        quest_files.append(file)
    end
end
puts("Found ", quest_files.len(), " Quest files")

File Size Check #

if io.exists("large_file.dat") and io.size("large_file.dat") > 1000000
    puts("Warning: File is larger than 1MB")
end