Reputation: 21
I want to open a large XML file in Clojure, process the text line per line then dump a selection of strings into a work file.
I am totally new to this so for now I just want to be able to (1) read a file, and (2) assign the selection to a variable. I will be working with large XML files so slurping is not an option.
Anyway, I'm using this code I got from a tutorial. Whenever I execute it in REPL, it prints out the text contained in my file, but seems to fail with creating the variable str1(unresolved symbol error).
Here's the code:
(defn readfile []
(let [str1 ;; I want the text inside the text file to fill this variable
(with-open [rdr (io/reader "resources/loremipsum.txt")]
(reduce conj [] (line-seq rdr)))] str1))
(readfile)
Upvotes: 2
Views: 379
Reputation: 2968
If you're trying to deal with xml in clojure, I'd recommend clojure.data.zip. Here's a quick example:
(require '[clojure.xml :as xml])
(require '[clojure.zip :as zip])
(require '[clojure.data.zip.xml :as zf])
(import '[java.io ByteArrayInputStream])
(with-open [in-stream (ByteArrayInputStream. (.getBytes "
<xml>
<test>something</test>
<fish>dog</fish>
<test>something else</test>
</xml>"))]
(let [parsed (zip/xml-zip (xml/parse in-stream))]
(zf/xml-> parsed :xml :test zf/text)))
It will produce a lazy sequence of the selector provided. It's quite often a really nice way to work with XML, and may suit your needs.
Upvotes: 1
Reputation: 91597
the code you have listed here is correct, did something get changed in copying and pasting it in?
user=> (require '[clojure.java.io :as io])
nil
(defn readfile []
(let [str1 (with-open [rdr (io/reader "lorum-lispsum")]
(reduce conj [] (line-seq rdr)))]
str1))
#'user/readfile
user=> (readfile)
["let lambda bind lambda let lambda"]
the patern (reduce conj...)
is often more easily written with the into
function.
(defn readfile []
(let [str1 (with-open [rdr (io/reader "lorum-lispsum")]
(into [] (line-seq rdr)))]
str1))
and since the effect of the into
here is both to make it a vector and to pull the entire file into memory simultaneously it could be replaced with a call to vec
(defn readfile []
(let [str1 (with-open [rdr (io/reader "lorum-lispsum")]
(vec (line-seq rdr)))]
str1))
since you also mention that not storing the whole file in memory is a goal, we may want to rearrange things to keep the file open while the sequence is processed as a lazy sequence
(require '[clojure.java.io :as io])
(defn process-a-line [line]
:your-code-here)
(defn the-main-part-of-my-program [lazy-sequence-of-lines]
(dorun (map process-a-line)))
(defn readfile []
(let [str1 (with-open [rdr (io/reader "lorum-lispsum")]
(line-seq rdr))]
(the-main-part-of-my-program str1)))
Upvotes: 2
Reputation: 144206
Bindings declared within a let
are only visible within the let
body. Since your readfile
function returns the lines of the file you can create a var and assign the result of calling the function to it:
(def lines (readfile))
Upvotes: 0