Reading Files in Racket

Our first program will demonstrate file reading operations in Racket. Here’s the full source code with explanations:

#lang racket

(require racket/file)

; Helper function to check for errors
(define (check result)
  (when (not result)
    (error "An error occurred")))

; Main function
(define (main)
  ; Read entire file contents
  (let ([dat (file->string "/tmp/dat")])
    (printf "~a" dat))

  ; Open file for more controlled reading
  (let ([f (open-input-file "/tmp/dat")])
    ; Read some bytes from the beginning of the file
    (let ([b1 (read-bytes 5 f)])
      (printf "~a bytes: ~a\n" (bytes-length b1) (bytes->string/utf-8 b1)))

    ; Seek to a known location and read from there
    (file-position f 6)
    (let ([b2 (read-bytes 2 f)])
      (printf "~a bytes @ ~a: ~a\n" (bytes-length b2) 6 (bytes->string/utf-8 b2)))

    ; Seek relative to current position
    (file-position f 4 'relative)

    ; Seek relative to end of file
    (file-position f -10 'end)

    ; Use port->bytes for robust reading
    (file-position f 6)
    (let ([b3 (port->bytes f 2)])
      (printf "~a bytes @ ~a: ~a\n" (bytes-length b3) 6 (bytes->string/utf-8 b3)))

    ; Rewind to beginning of file
    (file-position f 0)

    ; Use buffered input port for efficiency
    (let* ([buffered-port (make-buffered-input-port f 1024)]
           [b4 (peek-bytes 5 0 buffered-port)])
      (printf "5 bytes: ~a\n" (bytes->string/utf-8 b4)))

    ; Close the file
    (close-input-port f)))

(main)

To run the program, save the code in a file (e.g., reading-files.rkt) and use the Racket interpreter:

$ echo "hello" > /tmp/dat
$ echo "racket" >> /tmp/dat
$ racket reading-files.rkt
hello
racket
5 bytes: hello
2 bytes @ 6: ra
2 bytes @ 6: ra
5 bytes: hello

This Racket program demonstrates various file reading operations:

  1. Reading an entire file into memory using file->string.
  2. Opening a file and reading specific portions using open-input-file and read-bytes.
  3. Seeking to different positions in the file with file-position.
  4. Using port->bytes for more robust reading.
  5. Creating a buffered input port with make-buffered-input-port for efficient reading.
  6. Peeking at bytes without consuming them using peek-bytes.

Note that Racket’s file I/O operations are different from those in many other languages. Instead of methods on file objects, Racket uses functions that operate on port objects. The concepts of seeking and reading are similar, but the syntax and exact function names differ.

Remember to properly close files when you’re done with them to free up system resources.

Next, we’ll look at writing files in Racket.