2012-11-30 11 views
5

Wiem, że istnieje wiele pokrewnych pytań, przeczytałem je, ale wciąż nie zdobyłem fundamentalnego zrozumienia, jak czytać-pisać-procesować. Weźmy na przykład następującą funkcję, która wykorzystuje clojure-csv biblioteki do analizowania liniiPrzeczytaj csv na liście w clojure

(defn take-csv 
    "Takes file name and reads data." 
    [fname] 
    (with-open [file (reader fname)] 
    (doseq [line (line-seq file)] 
     (let [record (parse-csv line)])))) 

Co chciałbym uzyskać to dane wczytywane do pewnego zbioru w wyniku (def data (take-csv "file.csv")) a później je przetwarzać. Więc w zasadzie moje pytanie brzmi: jak mogę zwrócić record lub raczej listę rekordów.

Odpowiedz

7

"doseq" jest często używany do operacji z efektem ubocznym. W twoim przypadku, aby stworzyć kolekcję płyt można użyć „map”:

(defn take-csv 
    "Takes file name and reads data." 
    [fname] 
    (with-open [file (reader fname)] 
    (doall (map (comp first csv/parse-csv) (line-seq file))))) 

lepiej analizować cały plik na nich w celu zmniejszenia kod:

(defn take-csv 
    "Takes file name and reads data." 
    [fname] 
    (with-open [file (reader fname)] 
    (csv/parse-csv (slurp file)))) 

Można również użyć zamiast clojure.data.csv z clojure-csv.core. Zmienia nazwę pliku parse-csv tylko na csv w poprzedniej funkcji.

(defn put-csv [fname table] 
    (with-open [file (writer fname)] 
    (csv/write-csv file table))) 
+0

Nice. Teraz pozostaje tylko "put-csv", pomożesz? –

+0

Dodałem to. – mobyte

+0

Zwróć uwagę na opcję: można użyć do wymuszenia jakichkolwiek efektów. Przechodzi kolejne kolejne z seq, zachowuje głowę i zwraca ją, co powoduje, że cały seq znajduje się w pamięci w tym samym czasie. – micrub

2

Z wszystkich rzeczy można zrobić z plikami .csv, sugeruję używanie clojure-CSV lub clojure.data.csv. Używam głównie clojure-csv do odczytu w pliku .csv.

Oto niektóre fragmenty kodu z biblioteki narzędziowej używanej w większości moich programów Clojure.

from util.core 

    (ns util.core 
     ^{:author "Charles M. Norton", 
     :doc "util is a Clojure utilities directory"} 

     (:require [clojure.string :as cstr]) 
     (:import java.util.Date) 
     (:import java.io.File) 
     (:use clojure-csv.core)) 

(defn open-file 
"Attempts to open a file and complains if the file is not present." 

[file-name] 
(let [file-data (try 
       (slurp file-name) 
       (catch Exception e (println (.getMessage e))))] 
    file-data)) 

(defn ret-csv-data 
"Returns a lazy sequence generated by parse-csv. 
Uses open-file which will return a nil, if 
there is an exception in opening fnam. 

parse-csv called on non-nil file, and that 
data is returned." 

[fnam] 
(let [csv-file (open-file fnam) 
     inter-csv-data (if-not (nil? csv-file) 
         (parse-csv csv-file) 
         nil) 

     csv-data 
     (vec (filter #(and pos? (count %) 
      (not (nil? (rest %)))) inter-csv-data))] 

    (if-not (empty? csv-data) 
     (pop csv-data) 
     nil))) 

(defn fetch-csv-data 
    "This function accepts a csv file name, and returns parsed csv data, 
    or returns nil if file is not present." 

    [csv-file] 
     (let [csv-data (ret-csv-data csv-file)] 
      csv-data)) 

Po przeczytaniu pliku .csv, co zrobić z jego zawartością, to inna sprawa. Zwykle biorę .csv "raporty" z jednego systemu finansowego, takie jak oceny nieruchomości, i formatowanie danych, które mają być przesłane do bazy danych innego systemu finansowego, np. Fakturowanie.

Będę często zipmap każdy wiersz .csv, więc mogę wyodrębnić dane według nazwy kolumny (po przeczytaniu w nazwach kolumn), a nawet wykonać sekwencję wierszy "ped .csv zipmap".

+0

Dziękuję. Wciąż byłoby interesujące usłyszeć twój komentarz do czytania całego pliku w pamięci, co może być problemem dla [bardzo dużych plików] (http://clojurewise.blogspot.com/2011/02/reading-csv-files.html). –

+0

O ile mi wiadomo, jest to leniwy ciąg map. clojure-csv zwraca leniwą sekwencję, a także zipmap. Tak to robię. – octopusgrabbus