Table of Contents

Files

Why files?

So far, all our programs lose their data when they finish running. Variables only exist while the program is alive. But what if you want to save a high score, store a configuration, or process a log? That is where files come in.

Files let your programs persist data — save it to disk so it survives after the program exits, and read it back later.

Reading a file

The simplest operation is reading an entire file into a string:

import { read_file } from "std/file"

fn main() {
    let content: String = read_file("message.txt")
    print(content)
}

If message.txt contains Hello from a file!, the program prints exactly that.

read_file returns the entire file as a single string, including newlines. This is the easiest way to get data from a file.

Writing a file

To save data, use write_file:

import { write_file } from "std/file"

fn main() {
    write_file("output.txt", "Nyx was here!")
    print("File written successfully")
}

This creates output.txt (or overwrites it if it already exists) with the text Nyx was here!.

write_file returns 1 on success.

Checking if a file exists

Before reading a file, you might want to check if it actually exists:

import { read_file, file_exists } from "std/file"

fn main() {
    if file_exists("config.txt") {
        let config: String = read_file("config.txt")
        print("Config loaded: " + config)
    } else {
        print("No config file found, using defaults")
    }
}

file_exists returns true if the file is on disk, false otherwise.

Working with lines

Files often contain multiple lines of data. You can use .split() to break the content into lines:

import { read_file } from "std/file"

fn main() {
    let content: String = read_file("names.txt")
    let lines: Array = content.split("\n")

    var i: int = 0
    while i < lines.length() {
        print("Line " + int_to_string(i + 1) + ": " + lines[i])
        i += 1
    }
}

If names.txt contains:

Alice
Bob
Charlie

Output:

Line 1: Alice
Line 2: Bob
Line 3: Charlie

Writing multiple lines

To write multiple lines, join them with newline characters:

import { write_file } from "std/file"

fn main() {
    let names: Array = ["Alice", "Bob", "Charlie", "Diana"]

    var content: String = ""
    var i: int = 0
    while i < names.length() {
        content = content + names[i] + "\n"
        i += 1
    }

    write_file("names.txt", content)
    print("Wrote " + int_to_string(names.length()) + " names")
}

Practical example: a simple config file

Many programs use config files to store settings. Let's build a simple key=value config reader:

import { read_file, write_file, file_exists } from "std/file"

fn load_config(path: String) -> Map {
    var config: Map = Map.new()

    if file_exists(path) {
        let content: String = read_file(path)
        let lines: Array = content.split("\n")

        var i: int = 0
        while i < lines.length() {
            let line: String = lines[i]
            let eq_pos: int = line.indexOf("=")
            if eq_pos > 0 {
                let key: String = line.substring(0, eq_pos)
                let value: String = line.substring(eq_pos + 1, line.length())
                config.insert(key, value)
            }
            i += 1
        }
    }

    return config
}

fn save_config(path: String, config: Map) {
    var content: String = ""
    // We save known keys
    if config.contains("host") {
        content = content + "host=" + config.get("host") + "\n"
    }
    if config.contains("port") {
        content = content + "port=" + config.get("port") + "\n"
    }
    if config.contains("debug") {
        content = content + "debug=" + config.get("debug") + "\n"
    }
    write_file(path, content)
}

fn main() {
    let config: Map = load_config("app.conf")

    if config.contains("host") {
        print("Host: " + config.get("host"))
    } else {
        print("No config found, creating defaults...")
        var defaults: Map = Map.new()
        defaults.insert("host", "localhost")
        defaults.insert("port", "8080")
        defaults.insert("debug", "false")
        save_config("app.conf", defaults)
        print("Default config saved to app.conf")
    }
}

Practical example: a simple logger

import { read_file, write_file, file_exists } from "std/file"

fn log_message(path: String, message: String) {
    var content: String = ""
    if file_exists(path) {
        content = read_file(path)
    }
    content = content + message + "\n"
    write_file(path, content)
}

fn main() {
    let log_file: String = "app.log"

    log_message(log_file, "Application started")
    log_message(log_file, "Processing data...")
    log_message(log_file, "Done!")

    print("Log contents:")
    print(read_file(log_file))
}

Output:

Log contents:
Application started
Processing data...
Done!

Practical example: CSV data processing

CSV (Comma-Separated Values) is a common file format. Let's read and process one:

import { read_file } from "std/file"

struct Product {
    name: String,
    price: int,
    quantity: int
}

fn parse_csv(content: String) -> Array {
    let lines: Array = content.split("\n")
    var products: Array = []

    // Skip header line, start at 1
    var i: int = 1
    while i < lines.length() {
        let line: String = lines[i]
        if line.length() > 0 {
            let fields: Array = line.split(",")
            let p: Product = Product {
                name: fields[0],
                price: string_to_int(fields[1]),
                quantity: string_to_int(fields[2])
            }
            products.push(p)
        }
        i += 1
    }

    return products
}

fn main() {
    // Assume inventory.csv contains:
    // name,price,quantity
    // Apple,2,50
    // Bread,3,20
    // Milk,4,30
    let content: String = read_file("inventory.csv")
    let products: Array = parse_csv(content)

    var total_value: int = 0
    var i: int = 0
    while i < products.length() {
        let p: Product = products[i]
        let value: int = p.price * p.quantity
        print(p.name + ": $" + int_to_string(value))
        total_value += value
        i += 1
    }

    print("Total inventory value: $" + int_to_string(total_value))
}

Exercises

  1. Write a program that reads a file of numbers (one per line) and prints the sum, average, minimum, and maximum.
  1. Write a "todo list" program that saves tasks to todos.txt. It should be able to add a task (append a line) and display all tasks (read and print).
  1. Write a program that reads a text file and counts how many times each word appears. Print the word counts.
  1. Write a program that copies one file to another: read the source, write to the destination, and print a confirmation message.
  1. Write a program that reads a CSV with student names and grades, calculates the average for each student, and writes a summary report to a new file.

Summary

Next chapter: Closures and first-class functions →

← Previous: Imports and modules Next: Closures and first-class functions →