如果可能,我想将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)))))
答案 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
次呼叫合并到您在此处使用的每个传感器中,以防止保留这些资源,因为在一般情况下您无法知道当每个传感器完成读取条目时。