从zip文件中懒洋洋地读取每个条目

时间:2014-12-10 15:41:03

标签: clojure

如果可能,我想将zip文件中的文件条目读入字符串序列。目前我正在做这样的事情来打印目录名称,例如:

 (defn entries [zipfile]
   (lazy-seq
       (if-let [entry (.getNextEntry zipfile)]
            (cons entry (entries zipfile)))))


(defn with-each-entry [fileName f]
   (with-open [z (ZipInputStream. (FileInputStream. fileName))]
       (doseq [e (entries z)]
            ; (println (.getName e))
            (f e)
            (.closeEntry z))))

(with-each-entry  "tmp/my.zip"
  (fn [e] (if (.isDirectory e)
            (println (.getName e)))))

然而,这将遍历整个zip文件。我怎么能改变这个,所以我可以把前几个条目说成像:

(take 10 (zip-entries "tmp/my.zip"
  (fn [e] (if (.isDirectory e)
            (println (.getName e)))))

1 个答案:

答案 0 :(得分:2)

这似乎非常适合the new transducers in CLJ 1.7 您只需使用comp和通常没有seq / collection参数的seq-transformationing fns构建您想要的转换器。在您的示例中,
(comp (map #(.getName %)) (take 10))
(comp (filter #(.isDirectory %)) (map #(-> % .getName println)))

这将返回多个arities的函数,您可以在很多方面使用它。在这种情况下,您希望在条目序列上急切地减少它(以确保条目的实现发生在with-open内),因此您使用transduce(通过压缩我的一个clojure项目文件夹制作的示例zip数据) ):

(with-open [z (-> "training-day.zip" FileInputStream. ZipInputStream.)]
  (let[transform (comp (map #(.getName %)) (take 10))]
    (transduce transform conj (entries z))))
;;return value: [".gitignore" ".lein-failures" ".midje-grading-config.clj" ".nrepl-port" ".travis.yml" "project.clj" "README.md" "target/" "target/classes/" "target/repl-port"]

这里我用基函数conj进行转换,它产生了一个名字的向量。如果您希望换能器执行副作用而不返回值,则可以使用(constantly nil)等基本函数来执行此操作:

(with-open [z (-> "training-day.zip" FileInputStream. ZipInputStream.)]
  (let[transform (comp (filter #(.isDirectory %)) (map #(-> % .getName  println)))]
    (transduce transform (constantly nil) (entries z))))

给出输出:

target/
target/classes/
target/stale/
test/

这有一个潜在的缺点是,您可能需要手动将.closeEntry次呼叫合并到您在此处使用的每个传感器中,以防止保留这些资源,因为在一般情况下您无法知道当每个传感器完成读取条目时。