2

Clojure's (read-string) is really useful.

eg.

(read-string "{:a 1 :b 2} {:c 3 :d 4} [1 2 3]")

will give me the first object, the {:a 1 :b 2}

But how can I get the rest of string ie. "{:c 3 :d 4} [1 2 3]"

What's the reader equivalent of rest or drop ?

4 Answers 4

4

You can wrap the string in a StringReader, then wrap that in a PushbackReader, then read from that reader multiple times.

NB. the example below uses clojure.edn/read, as that is an edn-only reader meant for dealing with pure data; clojure.core/read is primarily meant for reading code and should never be used with untrusted inputs.

(require '[clojure.edn :as edn])

(def s "{:a 1 :b 2} {:c 3 :d 4} [1 2 3]")

;; Normally one would want to use with-open to close the reader,
;; but here we don't really care and we don't want to accidentally
;; close it before consuming the result:
(let [rdr (java.io.PushbackReader. (java.io.StringReader. s))
      sentinel (Object.)]      ; ← or just use ::eof as sentinel
  (take-while #(not= sentinel %)
              (repeatedly #(edn/read {:eof sentinel} rdr))))
;= ({:a 1, :b 2} {:c 3, :d 4} [1 2 3])
Sign up to request clarification or add additional context in comments.

Comments

2

ClojureScript version of what should be the accepted answer by https://stackoverflow.com/users/232707/michał-marczyk

(require '[cljs.reader :as rdr])
(require '[cljs.tools.reader.reader-types :as reader-types])

(def s "{:a 1 :b 2} {:c 3 :d 4} [1 2 3]")

(let [pbr (reader-types/string-push-back-reader s)
      sentinel ::eof]
    (take-while #(not= sentinel %)
        (repeatedly #(rdr/read {:eof sentinel} pbr))))

Comments

0

Probably not very idiomatic but straightforward

(->> (str "(" "{:a 1 :b 2} {:c 3 :d 4} [1 2 3]" ")") 
     (read-string))

then access to individual elements (you can also use brackets)

2 Comments

Yes, that works. Though it is kind of weird to have to do it that way ... cheers
The idiomatic and complete answer - which does not require manual changes to the input - has been provided by Michal Marczyk
0

If you have a list within the string, you can preserve it via options given to read-string-

(def str-list "({:a 1 :b 2} {:c 3 :d 4} [1 2 3])")

(read-string {:read-cond :preserve} str-list)
;;=> ({:a 1 :b 2} {:c 3 :d 4} [1 2 3])

The source for the available options can be found in the doc string the of read function, i.e. (source read)from the REPL.

2 Comments

read-cond has nothing to do with this at all. There are no reader conditionals in any of the example inputs.
You are correct insofar as there are no reader conditionals. However, the option hash allows the preservation of valid forms in the string, which is the functionality that the OP is after, with no additional libs. Perhaps :read-cond is too narrow of a name, but it parses the string according to what the question is looking for.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.