Home > front end >  Clojure - read text file and enter it as a list
Clojure - read text file and enter it as a list

Time:11-30

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