Chapter 7: I/O
This chapter covers reading from and writing to files and the console.
Console I/O
Output
For most output, use println with format strings:
(println "Hello, ~a!" name) ; Format and print with newline
(println "Got ~a items" (length items))
(print "Enter name: ") ; Print without newline (for prompts)The low-level primitives display, write, and newline are also available:
(display "Hello") ; Print without quotes or newline
(write "Hello") ; Print with quotes: "Hello" (for data)
(write '(1 2 3)) ; Print as data: (1 2 3)
(newline) ; Print a newlinedisplay is for human-readable output. write produces output that can be read back by read.
Input
(define input (read)) ; Read a Scheme expression
(define line (read-line)) ; Read a line as string
(define char (read-char)) ; Read a single characterExample interactive program:
(print "What is your name? ")
(define name (read-line))
(println "Hello, ~a!" name)Detecting End of Input
(let ((input (read)))
(if (eof-object? input)
(println "End of input")
(println "You entered: ~s" input))) ; ~s uses write formatFile I/O
Import the I/O library:
(import (sigil io))Opening Files
;; Text files
(define port (open-input-file "data.txt"))
(define out (open-output-file "output.txt"))
;; Binary files
(define bin-in (open-binary-input-file "image.png"))
(define bin-out (open-binary-output-file "copy.png"))Reading Files
;; Read one line
(define line (read-line port))
;; Read all lines
(let loop ()
(let ((line (read-line port)))
(unless (eof-object? line)
(process-line line)
(loop))))
;; Read Scheme expressions
(define expr (read port))
;; Read all expressions
(define exprs (read-all port))Writing Files
(display "Hello" port)
(write '(data to save) port)
(newline port)
(flush-output-port port) ; Ensure data is writtenClosing Files
(close-input-port port)
(close-output-port out)Safe File Handling
Use call-with-input-file to ensure files are closed:
(call-with-input-file "data.txt"
(lambda (port)
(let loop ((lines '()))
(let ((line (read-line port)))
(if (eof-object? line)
(reverse lines)
(loop (cons line lines)))))))Similarly for output:
(call-with-output-file "output.txt"
(lambda (port)
(display "Line 1" port)
(newline port)
(display "Line 2" port)
(newline port)))Practical Example: Config File
Read a simple key=value config file:
(import (sigil io)
(sigil string))
(define (read-config filename)
(call-with-input-file filename
(lambda (port)
(let loop ((config '()))
(let ((line (read-line port)))
(if (eof-object? line)
config
(let ((parts (string-split line "=")))
(if (= (length parts) 2)
(loop (cons (cons (string-trim (car parts))
(string-trim (cadr parts)))
config))
(loop config)))))))))
;; Usage:
;; (define config (read-config "app.conf"))
;; (assoc "username" config)Filesystem Operations
Import the filesystem library:
(import (sigil fs))Checking Files
(file-exists? "data.txt") ; => #t or #f
(file? "data.txt") ; Is it a regular file?
(directory? "mydir") ; Is it a directory?Directory Operations
(make-directory "newdir")
(directory-list ".") ; List files in directory
(current-directory) ; Get current directory
(set-current-directory! "/tmp")File Information
(file-size "data.txt") ; Size in bytes
(file-mtime "data.txt") ; Modification timeWalking Directories
(directory-walk "src"
(lambda (path type)
(println "~a (~a)" path type)
#t)) ; Return #t to descend into directoriesPath Manipulation
Import the path library:
(import (sigil path))Path Components
> (import (sigil path))
> (path-dirname "/home/user/file.txt")
"/home/user"
> (path-basename "/home/user/file.txt")
"file.txt"
> (path-extname "/home/user/file.txt")
".txt"Building Paths
> (path-join "home" "user" "file.txt")
"home/user/file.txt"
> (path-normalize "foo/bar/../baz")
"foo/baz"Path Predicates
> (path-absolute? "/home/user")
#t
> (path-absolute? "relative")
#fComplete Example: File Processor
A program that processes all .txt files in a directory:
(import (sigil io)
(sigil fs)
(sigil path)
(sigil string))
(define (process-text-file path)
(println "Processing: ~a" path)
(let ((lines (call-with-input-file path
(lambda (port)
(let loop ((lines '()))
(let ((line (read-line port)))
(if (eof-object? line)
(reverse lines)
(loop (cons line lines)))))))))
(println " Lines: ~a" (length lines))))
(define (process-directory dir)
(directory-walk dir
(lambda (path type)
(when (and (eq? type 'file)
(string=? (path-extname path) ".txt"))
(process-text-file path))
#t)))
(process-directory ".")Practice Exercises
- Write a program that counts words in a text file.
- Create a simple file backup utility.
- Write a program that finds all files larger than a given size.
What's Next
Let's learn about macros — code that writes code.