catherine.c.stwrt
2019-11-10 17:06:37

@catherine.c.stwrt has joined the channel


catherine.c.stwrt
2019-11-10 18:00:00

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 strings and ports 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?


soegaard2
2019-11-10 18:06:28

Use write to write a value. Use read to read it back.


catherine.c.stwrt
2019-11-10 18:07:34

I typed (test "It exists.") manually. I also tried it quoted.


soegaard2
2019-11-10 18:07:58
(with-output-to-file "foo.txt"
    (lambda () (write '(my example of a value))))
(with-input-from-file "foo.txt"
   (lambda () (read)))

catherine.c.stwrt
2019-11-10 18:08:37

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?


soegaard2
2019-11-10 18:10:07

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.


catherine.c.stwrt
2019-11-10 18:12:00

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?


soegaard2
2019-11-10 18:12:34

You forgot to wrap read in a lambda. (lambda () (read))


catherine.c.stwrt
2019-11-10 18:12:59

Oh, wait, really? Huh.


soegaard2
2019-11-10 18:13:08

Both with-output-to-file and with-input-from-file takes a “thunk” (a function of no arguments) as input.


soegaard2
2019-11-10 18:13:22

It’s very easy to forget - I still forget it when I am tired.


catherine.c.stwrt
2019-11-10 18:13:53

Oof, still getting the same error. Hmm.


soegaard2
2019-11-10 18:14:01

Which error?


soegaard2
2019-11-10 18:15:49

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))


soegaard2
2019-11-10 18:16:17

Or ’append if you want to write to the end of the file.


catherine.c.stwrt
2019-11-10 18:16:45

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.


soegaard2
2019-11-10 18:17:50

Is an end " missing?


catherine.c.stwrt
2019-11-10 18:18:03
#lang racket

(with-output-to-file "dellater.sexp"
  (lambda () (write '(what about one)))
  #:exists 'replace)
(println (with-input-from-file "dellater.sexp" (read)))

catherine.c.stwrt
2019-11-10 18:19:31

Oh gosh, for got the shebang.


soegaard2
2019-11-10 18:19:33

Forgot the (lambda () (read))


catherine.c.stwrt
2019-11-10 18:19:37

But now it’s an infinite loop.


catherine.c.stwrt
2019-11-10 18:20:41

ayyyyyy, it finally worked


catherine.c.stwrt
2019-11-10 18:20:42

oof


soegaard2
2019-11-10 18:21:08

It wasn’t an infinite loop, it was trying to read from standard input.


catherine.c.stwrt
2019-11-10 18:21:19

Oh, heh.


catherine.c.stwrt
2019-11-10 18:21:34

If I did switch to 'append instead and had a bunch of expressions in there, do I need to do anything differently?


soegaard2
2019-11-10 18:22:23

If you use ’append then you need to figure out how you want to read the data.


soegaard2
2019-11-10 18:23:01

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.


soegaard2
2019-11-10 18:23:34

If there are no more s-expressions to read, the end of file object will be returned.


catherine.c.stwrt
2019-11-10 18:24:34

I was messing around with loops. I’m not sure the equivalent of for thing in generator that I’d do in Python.


catherine.c.stwrt
2019-11-10 18:25:06

But at this point I might be able to figure it out.


catherine.c.stwrt
2019-11-10 18:25:17

Thanks so much! I would definitely not have figured that out.


soegaard2
2019-11-10 18:25:59

What kind of generator are you thinking of?


catherine.c.stwrt
2019-11-10 18:26:48

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.


catherine.c.stwrt
2019-11-10 18:27:53

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.


soegaard2
2019-11-10 18:28:15

I see. Perhaps you can use in-port. Example:


soegaard2
2019-11-10 18:28:58
(with-input-from-file "foo.txt"
   (lambda()
      (for/list ([x (in-port)])
         x)))

soegaard2
2019-11-10 18:29:32

The “generator” in-port will read from the current input port using read.


catherine.c.stwrt
2019-11-10 18:30:13

Oh, okay. I’ll try that.


catherine.c.stwrt
2019-11-10 18:31:36

Awesome! Just what I needed. Thanks!


samth
2019-11-10 20:52:14

I encourage the use of file->value and file->list in these situations, they’re much easier to use