Mojo File IO
File I/O (Input/Output) lets your program read data from files on disk and write results back. Reading configuration files, processing datasets, saving logs, and generating reports all require file I/O. Mojo provides the open() function and a FileHandle type for this purpose.
The Filing Cabinet Analogy
Filing Cabinet = Disk storage
Drawer label = File path ("data/scores.txt")
Open drawer = open("data/scores.txt", "r")
Read papers = file.read()
Close drawer = file.close()
Always close the drawer after you finish.
An open file holds system resources the OS needs back.
Opening and Reading a File
fn main() raises:
var file = open("hello.txt", "r") # "r" = read mode
var content = file.read()
file.close()
print(content)
The second argument to open() is the mode string:
Mode | Meaning -----|------------------------------------------------------- "r" | Read text (file must exist) "w" | Write text (creates or overwrites file) "a" | Append text (creates if missing, adds to end) "rb" | Read binary bytes "wb" | Write binary bytes
Writing to a File
fn main() raises:
var file = open("output.txt", "w")
file.write("Line 1: Hello from Mojo\n")
file.write("Line 2: File I/O works!\n")
file.write("Line 3: Done.\n")
file.close()
print("File written successfully")
Opening in "w" mode creates the file if it does not exist and erases all existing content if it does. Use "a" (append) to add content without erasing.
Using with for Automatic Close
The with statement opens a file and automatically closes it when the block ends — even if an error occurs inside the block. This is the recommended approach because it prevents resource leaks.
fn main() raises:
# Write phase
with open("numbers.txt", "w") as f:
for i in range(1, 6):
f.write(String(i) + "\n")
# File closed automatically here
# Read phase
with open("numbers.txt", "r") as f:
var data = f.read()
print(data)
Output:
1 2 3 4 5
Reading Line by Line
For large files, reading one line at a time avoids loading the entire file into memory at once.
fn main() raises:
with open("numbers.txt", "r") as f:
var line = f.readline()
while line != "":
var trimmed = line.strip()
if trimmed != "":
print("Read:", trimmed)
line = f.readline()
Output:
Read: 1 Read: 2 Read: 3 Read: 4 Read: 5
readline() behavior: First call → "1\n" Second call → "2\n" ... After last line → "" (empty string signals end of file)
Appending to an Existing File
fn main() raises:
# First run creates file with initial content
with open("log.txt", "w") as f:
f.write("Session started\n")
# Subsequent runs append without erasing
with open("log.txt", "a") as f:
f.write("New event logged\n")
# Read back the full content
with open("log.txt", "r") as f:
print(f.read())
Output:
Session started New event logged
CSV Processing Pattern
CSV files are the most common format for tabular data. Mojo reads them as text and you split lines on commas.
fn main() raises:
# Write a sample CSV
with open("students.csv", "w") as f:
f.write("name,score,grade\n")
f.write("Alice,95,A\n")
f.write("Bob,82,B\n")
f.write("Carol,74,C\n")
# Read and parse it
with open("students.csv", "r") as f:
var header = f.readline() # skip header
var line = f.readline()
while line != "":
var trimmed = line.strip()
if trimmed != "":
# Manual split on comma
var parts = trimmed.split(",")
print(parts[0], "scored", parts[1], "→ grade", parts[2])
line = f.readline()
Output:
Alice scored 95 → grade A Bob scored 82 → grade B Carol scored 74 → grade C
Checking if a File Exists
from os import path
fn main():
if path.exists("config.txt"):
print("Config file found")
else:
print("Config file missing — using defaults")
File Error Handling
File operations fail when the file is missing, permissions are denied, or the disk is full. Always wrap file operations in try/except for production code.
fn read_config(path: String) raises -> String:
try:
with open(path, "r") as f:
return f.read()
except e:
raise Error("Failed to read " + path + ": " + str(e))
fn main():
try:
var config = read_config("settings.txt")
print(config)
except e:
print("Using defaults:", str(e))
Key Takeaways
Open files with open(path, mode) where mode is "r", "w", or "a". Always close files — the with statement handles this automatically. Use read() for the entire file content, readline() for line-by-line processing of large files. Append mode adds to existing content without erasing it. Wrap file operations in try/except to handle missing files and permission errors gracefully. Check file existence with path.exists() before opening when the file is optional.
