@catherine.c.stwrt has joined the channel
I’m trying to read and write s-expressions to a plain text file, and I can’t figure it out. read
and eval
and string
s and port
s and call-with-input-file
and so on all have confusing type names and fail to work with each other seemingly at random. One StackOverflow post mentions that s-expression files have the file extension .rktd
(but I can’t find anything else online about that other than Dr Racket docs) and should be accessed with read
but the only snippet online I can find for that ((define my-list (call-with-input-file "file-that-contains-list.txt" read))
) crashes, printing literal data is not allowed;
.
This seems like it should be straightforward. Is there a simple way to do this? And gosh, what if I want to read more than just one s-expression from a file and put them all into a list?
Use write
to write a value. Use read
to read it back.
I typed (test "It exists.")
manually. I also tried it quoted.
(with-output-to-file "foo.txt"
(lambda () (write '(my example of a value))))
(with-input-from-file "foo.txt"
(lambda () (read)))
Does in not store it in plaintext? I should be able to edit the file manually and have it work if there isn’t a syntax mistake, right?
You can see what is written to the file with a few examples in the repl. > (write "foo")
"foo"
> (write 42)
42
Means that the string foo will be written as “foo” and the number 42 will be written as 42.
I just ran (with-output-to-file "dellater.sexp"
(lambda () (write '(what about this))))
(println (with-input-from-file "dellater.sexp" read))
and it didn’t modify the file at all, and I still got literal data is not allowed
. Maybe something’s messed up with my configuration?
You forgot to wrap read in a lambda. (lambda () (read))
Oh, wait, really? Huh.
Both with-output-to-file and with-input-from-file takes a “thunk” (a function of no arguments) as input.
It’s very easy to forget - I still forget it when I am tired.
Oof, still getting the same error. Hmm.
Which error?
If the file already exists, you might need to add #:exists 'replace
. As in (with-output-to-file "dellater.sexp"
(lambda () (write '(what about this)))
#:exists 'replace))
Or ’append if you want to write to the end of the file.
I’ll try that. I moved to a blank file to clean out potential other errors, but it’s saying Unterminated quoted string
so I’m trying to figure that out.
Is an end " missing?
#lang racket
(with-output-to-file "dellater.sexp"
(lambda () (write '(what about one)))
#:exists 'replace)
(println (with-input-from-file "dellater.sexp" (read)))
Oh gosh, for got the shebang.
Forgot the (lambda () (read))
But now it’s an infinite loop.
ayyyyyy, it finally worked
oof
It wasn’t an infinite loop, it was trying to read from standard input.
Oh, heh.
If I did switch to 'append
instead and had a bunch of expressions in there, do I need to do anything differently?
If you use ’append then you need to figure out how you want to read the data.
When you open the file and use (read), the first s-expression is read. If you use (read) again the second s-expression is read and so on.
If there are no more s-expressions to read, the end of file object will be returned.
I was messing around with loops. I’m not sure the equivalent of for thing in generator
that I’d do in Python.
But at this point I might be able to figure it out.
Thanks so much! I would definitely not have figured that out.
What kind of generator are you thinking of?
My understanding is that Python generators work basically like what you described. You call them over and over, they keep returning the next thing, until they return something (I forget what) to signal the end.
So if there were some function in Python that generates expressions from a file, I’d just do for sexp in read_file: list.append(sexp)
or actually probably a list comprehension.
I see. Perhaps you can use in-port
. Example:
(with-input-from-file "foo.txt"
(lambda()
(for/list ([x (in-port)])
x)))
The “generator” in-port
will read from the current input port using read.
Oh, okay. I’ll try that.
Awesome! Just what I needed. Thanks!
I encourage the use of file->value and file->list in these situations, they’re much easier to use