You want to
write data to a file such that when you
eval that file that data is recreated.
Use the functions provided by the
pconvert library to convert data into a format, that when read, will recreate the data. For example, the
print-convert function converts data to quasi-quoted form:
> (require (lib "pconvert.ss"))
> (define data '(1 2 3 #(a b)))
> (print-convert data)
(quasiquote (1 2 3 #2(a b)))
> (eval (print-convert data))
(1 2 3 #2(a b))
> (equal? (eval (print-convert data)) data)
#t
> (eq? (eval (print-convert data)) data)
#f
You might wonder we don't just use
write and
read instead of
print-convert. There are two reasons for this. Firstly, the range of data that can be represented by
print-convert is greater than that that can be represented by
write. See, for instance,
StructuresReadingAndWriting which discusses reading and writing structures using
print-convert.
More importantly
print-convert=ed data can be intermingled with program code. For example, if we have the list ='(+ 1 2) then if we
write and
eval this data we get the value
3. However if we first
print-convert the data and then
eval it we get the back the original list:
> (define data '(+ 1 2))
> (write data)
(+ 1 2)
> (print-convert data)
`(+ 1 2)
> (let ((port (open-output-string)))
(write data port)
(eval (read (open-input-string (get-output-string port)))))
3
> (eval (print-convert data))
(+ 1 2)
This allows, for example, programmatic configuration files that are a mixture of code and print-converted data. This idea is sometimes known as an
Active File. For example:
> (eval `(map + ,(print-convert '(1 2 3)) ,(print-convert '(3 4 5))))
(4 6 8)
Two convenience functions to write and read (eval) files are:
(define (write-converted data filename)
(with-output-to-file filename
(lambda ()
(write (print-convert data)))
'replace))
(define (read-converted filename)
(with-input-from-file filename
(lambda ()
(eval (read)))))
In use:
> (write-converted '(1 2 3 #(a b)) "test.txt")
> (read-converted "test.txt")
(1 2 3 #2(a b))
Note that
load can take the place of
read-converted in most situations.
Load will
eval all expressions in a file, while
read-converted will only
eval the first. In this way
read-converted is the mirror of
write-converted, which only writes a single expression to a file.
To create human-readable output (for instance, in configuration files) the
pretty-print function in the
pretty.ss library comes in handy:
> (require (lib "pretty.ss"))
> (pretty-print (print-convert '(some (complex data) that (we (would like to (span))) a (number of lines (so that it is) (human readable)))))
`(some
(complex data)
that
(we (would like to (span)))
a
(number of lines (so that it is) (human readable)))
See
IdiomPrettyPrint? for more information.
Finally if using
eval you should be careful that you protect your code and the user's computer against malicious code. See
IdiomUntrustedEval? for information on how to do this.
A number of recipes are mentioned in the discussion:
Integrated all the comments into the text.
--
NoelWelsh - 23 Dec 2004
--
NoelWelsh
--
DanielSilva
--
JensAxelSoegaard