I am having trouble with some basic IO operations using Clojure. I have a text file which I need to read, split with the "|" character, and enter into a list for later processing. Here are the contents of my text file:
1|John Smith|123 Here Street|456-4567
2|Sue Jones|43 Rose Court Street|345-7867
3|Fan Yuhong|165 Happy Lane|345-4533
And here is my current code:
((defn -main []
(println "Enter an option: \n")
(let [choice (read-line)]
(cond (= choice "1")
(let [cust-contents (slurp "file.txt")
nums-as-strings (clojure.string/split cust-contents #"|")
numbers (map read-string nums-as-strings)]
(print numbers)
)
)
) ) )
(-main)
I would think this code to work, however here is the error I get when running my program:
(; Execution error at user/eval7923$-main (REPL:11).
; EOF while reading
Could anyone please guide me on where I went wrong and on how to fix this?
CodePudding user response:
For one thing, you should not have 2 left parentheses in a row like ((defn ...
.
Also, CSV parsing has many libraries to remove the drudgery of reinventing the wheel. My favorite is illustrated below:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[tupelo.csv :as csv]))
(verify
; ***** NOTE ***** most CSV has a header line, which is added below for convenience!
(let [data-str "id|name|address|phone
1|John Smith|123 Here Street|456-4567
2|Sue Jones|43 Rose Court Street|345-7867
3|Fan Yuhong|165 Happy Lane|345-4533 "
entity-maps (csv/csv->entities data-str {:separator \|})]
(is= entity-maps
[{:address "123 Here Street", :id "1", :name "John Smith", :phone "456-4567"}
{:address "43 Rose Court Street", :id "2", :name "Sue Jones", :phone "345-7867"}
{:address "165 Happy Lane", :id "3", :name "Fan Yuhong", :phone "345-4533"}])))
This unit test leaves out the file-reading part as that just complicates things in a unit test.
The above solution is based on my favorite template project, which also has a list of documentation sources toward the end.
You can find examine the source code for tupelo.csv and also the unit tests.
To clarify your code, here is a cleaned-up version:
(verify
(let [data-str "1|John Smith|123 Here Street|456-4567
2|Sue Jones|43 Rose Court Street|345-7867
3|Fan Yuhong|165 Happy Lane|345-4533 "
data-lines (str/split-lines data-str)
token-table-str (vec (for [line data-lines]
(let [token-strs (str/split line #"\|")]
(mapv str/trim token-strs))))]
(is= token-table-str
[["1" "John Smith" "123 Here Street" "456-4567"]
["2" "Sue Jones" "43 Rose Court Street" "345-7867"]
["3" "Fan Yuhong" "165 Happy Lane" "345-4533"]])))
Notice that I didn't parse the data past the String stage.
CodePudding user response:
That error is actually caused when you call read-string
with argument " "
.
And where that " "
comes from? You used the wrong regex in clojure.string/split
, you should use #"\|"
instead of #"|"
.
And even then, you can't call read-string
on everything, as it would soon crash again, trying to parse "456-4567"
.
Also, ending parentheses belong to the same line.
If your file contains newlines, you will need clojure.string/split-lines
and if you will also implement 2 Display Product Table, 3. Display Sales Table
and so on (I know the context for this code), case
will be better than cond
.
Here is your code with some improvements:
(ns homework.core
(:require [clojure.string :as s])
(:gen-class))
(defn -main []
(println "Enter an option: \n")
(let [choice (read-line)]
(case choice
"1" (->> (s/split-lines (slurp "file.txt"))
(mapv #(s/split % #"\|"))
println)
"Wrong number")))
(-main)