[plt-scheme] input from file

From: Matthias Felleisen (matthias at ccs.neu.edu)
Date: Sat Apr 3 09:42:44 EST 2004

On Apr 3, 2004, at 6:26 AM, Markus Spath wrote:

>  For list-related administrative tasks:
>  http://list.cs.brown.edu/mailman/listinfo/plt-scheme
>
> hi,
>
> I need some help reading data from a file.
>
> currently I'm hardcoding data-strucures I want to deal with:
>
> --
> ;; struct question
> ;; name is a symbol; v1, v2 are numbers
> (define-struct question (name v1 v2))
>
> ;; questions-hardcoded: -> list of questions
> ;; generates a list of questions
> (define (questions-hardcoded)
>   (list
>    (make-question 'q1 10 20)
>    (make-question 'q2 5 13)
>    (make-question 'q3 5 8)
>    ...))
>
> (define questions (questions-hardcoded))
> --
>
> It would be nice to have a function that builds a list of questions 
> from a file, like:
>
> --
> ;; questions-from-file: string -> list of questions
> ;; generates a list of questions from a file
> (define (questions-from-file filename)
>   ... )
>
> (define questions2 (questions-from-file "C:\\home\\scheme\\data.txt"))
> --
>
> data.txt:
> --
> q1 10 20
> q2 5 13
> q3 5 8
> ...
> --
>
>
>
> I've just started recently to teach myself scheme via htdp/DrScheme 
> and all examples for I/O I found in the help-desk or via google use 
> syntactic constructs I don't know yet; an idiot-proof and minimalistic 
> example of how to read a file line by line and how to tokenize a 
> string (at whitespace) into symbols and/or numbers would be of great 
> help.

Here is the first part: reading from a file as close as possible to the 
HtDP style:

;; File is one of:
;; -- end of file object
;; -- String followed by File

;; predicate: eof-object? : Any -> Boolean
;;            is the given value the end of file (EOF) object
;; accessor:  read-line : -> String or EOF
;;            compute the first string from standard input or EOF
;;            with-input-from-file : String (-> X) -> X
;;            make first argument the standard input for the second one

;; String -> (Listof String)
;; read filename into a list of lines
(define (read-all filename)
   (local (;; -> (Listof String)
           ;; read all lines from standard input
           (define (auxiliary)
             (local ((define next-line (read-line)))
               (cond
                 [(eof-object? next-line) empty]
                 [else (cons next-line (auxiliary))]))))
     (with-input-from-file filename auxiliary)))

(read-all "foo.ss")

Now that you have a list of strings, let's tokenize each of them:

;; Char (Listof String) -> (Listof (Listof String))
;; split a list of lines (strings) into token at c boundary
;; (splitter* #\space '("hello world" "good bye"))
;; becomes '(("hello" "world") ("good" "bye"))
(define (splitter* c los)
   (map (lambda (x) (splitter c x)) los))

;; Char String -> (Listof String)
;; split a string (line) into tokens at character c
;; (splitter #\space "hello world")
;; becomes '("hello" "world")
(define (splitter c s)
   (regexp-split (string c) s))

So (splitter* #\space (read-all "foo.ss")) will tokenize the file 
foo.ss.
(I called the program above "foo.ss" so it tokenized itself. You don't 
get
extra cost that way.)

And finally you may want to turn lines into records a la HtDP:

(define-struct person (name yob) (make-inspector))
;; Person is (make-person String Number)

;; Char String -> (make-person String Number)
;; split a string into a string and a number at c
(define (line->record c line)
   (local ((define s (splitter c line))
           (define n (string->number (second s))))
     (cond [(number? n) (make-person (first s) n)]
           [else (error 'line->record "bad input: ~e" s)])))

;; Example/Tests:
(equal? (line->record #\space "hello 2") (make-person "hello" 2))

-- Matthias





Posted on the users mailing list.