我如何从文件反序列化记录结构,已经保存到print-dup文件?

时间:2013-09-22 17:55:10

标签: clojure

我有以下代码:

(use 'clojure.java.io)
(defrecord Member [id name salary role])  
(defrecord Role [id name])
(def member-records (ref ()))
(defn add-member [member]
(dosync (alter member-records conj member)))

;;Test-data -->
(def dev-r(->Role 1 "Developer"))
(def test-member1(->Member 1 "Kirill" 70000.00 dev-r))
;;Test-data <--

(defn save-data-2-file []
  (with-open [wrtr (writer "C:/Platform/Work/test.cdf")]  
    (print-dup @member-records wrtr)))

(defn process-line [line]
  (println line))

 ;;Test line content
 ;;#BTC.pcost.Member{:id 1, :name "Kirill", :salary 70000.0, :role #BTC.pcost.Role{:id 1, :name "Developer"}})

(defn load-data-from-file []
  (with-open [rdr (reader "C:/Platform/Work/test.cdf")]
   (doseq [line (line-seq rdr)]
     (process-line line))))

我想在阅读文件后重新创建记录,但我无法理解我是如何做到的。是的,我知道我可以通过解析行的元素来解析文本并填充我的结构,但是它会很困难,因为我有很多像“成员”和“角色”这样的结构。任何人都可以建议我,我能做什么?

2 个答案:

答案 0 :(得分:3)

您可以使用read-stringslurp将记录从文件中提取出来。 read-string仅限于读取字符串的第一种形式,但是,从您的示例中,您只存储单个表单,作为记录列表。

(defn load-data-from-file [file]
  (read-string (slurp file)))


懒惰阅读

如果您需要的不仅仅是第一个表单,或者无法将整个流读入内存,您可以直接使用read来制作一个懒惰的读者。

(defn lazy-read
  ([rdr] (let [eof (Object.)] (lazy-read rdr (read rdr false eof) eof)))
  ([rdr data eof]
    (if (not= eof data)
      (cons data (lazy-seq (lazy-read rdr (read rdr false eof) eof))))))

(defn load-all-data [file]
  (with-open [rdr (java.io.PushbackReader. (reader file))]
    (doall (lazy-read rdr))))

(load-all-data "C:/Platform/Work/test.cdf")


安全

此外,在使用read-stringread加载代码时,最好提及安全性。您只应将它们与受信任的源一起使用,因为使用#=或Java构造函数,源可以在您的应用程序中执行任意代码。有关更长的说明,请查看read的文档。

*read-eval*设置为false会阻止此问题,但也会阻止重建样本中的记录。要一起避免此问题,您可以使用clojure.edn/readclojure.edn/read-string功能以及读者白名单。

(defn edn-read [eof rdr]
  (clojure.edn/read {:eof eof :readers {'BTC.pcost.Role map->Role
                                        'BTC.pcost.Member map->Member}}
                    rdr))

(defn lazy-edn-read
  ([rdr] (let [eof (Object.)] (lazy-edn-read rdr (edn-read eof rdr) eof)))
  ([rdr data eof]
   (if (not= eof data)
     (cons data (lazy-seq (lazy-edn-read rdr (edn-read eof rdr) eof))))))

(defn load-all-data [file]
  (with-open [rdr (java.io.PushbackReader. (reader file))]
    (doall (take-while (complement nil?) (lazy-edn-read rdr)))))

(load-all-data "C:/Platform/Work/test.cdf")

答案 1 :(得分:2)

您可以使用read

此函数将从文件中读取一个对象:

(defn load-data-from-file [filename]
  (with-open [rdr (java.io.PushbackReader. (reader filename))]
    (read rdr)))

或者这将读取文件中的所有对象:

(defn load-all-data-from-file [filename]
  (let [eof (Object.)]
    (with-open [rdr (java.io.PushbackReader. (reader filename))]
      (doall
       (take-while #(not= % eof)
                   (repeatedly #(read rdr nil eof)))))))

Here's read的API文档。

这是一个小变体,它将读取字符串中的所有对象:

(defn load-all-data-from-string [string]
  (let [eof (Object.)]
    (with-open [rdr (-> string java.io.StringReader. java.io.PushbackReader.)]
      (doall
       (take-while #(not= % eof)
                   (repeatedly #(read rdr nil eof)))))))

据我所知,这是不可能使用read-string。相反,我们将readjava.io.StringReader一起使用。