我正在尝试在clojure中重写以下片段,但这一切都很丑陋,也许有人会提出更优雅的解决方案?
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ZipFileRdrExp {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("C:\\MyZip.zip");
ZipInputStream zis = new ZipInputStream(fis);
ZipEntry ze;
while((ze=zis.getNextEntry())!=null){
System.out.println(ze.getName());
zis.closeEntry();
}
zis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
这是我对丑陋的尝试,重复调用getNextEntry:
(ns app.core
(:import
(java.io FileInputStream FileNotFoundException IOException File)
(java.util.zip ZipInputStream ZipEntry)))
(defn- read-zip [zip-file]
(let [fis (FileInputStream. zip-file)
zis (ZipInputStream. fis)]
(loop [ze (.getNextEntry zis)]
(when ze
(println (.getName ze))
(.closeEntry zis)
(recur (.getNextEntry zis))))
(.close zis)))
答案 0 :(得分:17)
我会选择以下内容:
(defn entries [zipfile]
(lazy-seq
(if-let [entry (.getNextEntry zipfile)]
(cons entry (entries zipfile)))))
(defn walkzip [fileName]
(with-open [z (ZipInputStream. (FileInputStream. fileName))]
(doseq [e (entries z)]
(println (.getName e))
(.closeEntry z))))
编辑:上述代码最终经过测试和更正。
编辑:以下按预期工作,它更简洁,即使它使用不同的Java API
(defn entries [zipfile]
(enumeration-seq (.entries zipfile)))
(defn walkzip [fileName]
(with-open [z (java.util.zip.ZipFile. fileName)]
(doseq [e (entries z)]
(println (.getName e)))))
答案 1 :(得分:6)
这是一个更简单的例子:
(defn filenames-in-zip [filename]
(let [z (java.util.zip.ZipFile. filename)]
(map #(.getName %) (enumeration-seq (.entries z)))))
这与上面的代码类似,但没有理由在这里使用with-open。此示例返回一系列数据,然后您可以打印出来或更好地格式化。最好让提取数据的函数只返回数据,而不是将打印的副作用包含在该函数中。
如果要打印出内容,可以使用
(pprint (filenames-in-zip "my.zip"))
它会给你一个很好的清单。
答案 2 :(得分:1)
这与使用ZipInputStream
的skuro答案类似,但对entries
的定义稍微简洁一些。
(defn entries [zip-stream]
(take-while #(not (nil? %))
(repeatedly #(.getNextEntry zip-stream))))
(defn walkzip [fileName]
(with-open [z (ZipInputStream. (FileInputStream. fileName))]
(doseq [e (entries z)]
(println (.getName e))
(.closeEntry z))))
或者,如果您想要实际提取文件,则需要另一个辅助函数进行复制。我已经使用clojure.java.io
来缩短代码,但是如果没有这种依赖,也可以完成同样的事情。
(require '[clojure.java.io :as io])
(defn entries [zip-stream]
(take-while #(not (nil? %))
(repeatedly #(.getNextEntry zip-stream))))
(defn copy-file [zip-stream filename]
(with-open [out-file (file-out-stream filename)]
(let [buff-size 4096
buffer (byte-array buff-size)]
(loop [len (.read zip-stream buffer)]
(when (> len 0)
(.write out-file buffer 0 len)
(recur (.read zip-stream buffer)))))))
(defn extract-stream [zip-stream to-folder]
(let [extract-entry (fn [zip-entry]
(when (not (.isDirectory zip-entry))
(let [to-file (io/file to-folder
(.getName zip-entry))
parent-file (io/file (.getParent to-file))]
(.mkdirs parent-file)
(copy-file zip-stream to-file))))]
(->> zip-stream
entries
(map extract-entry)
dorun)))
这实际上等同于使用unzip
实用程序简单地解压缩文件。它的美妙之处在于,由于条目是懒惰的seq,您可以filter
或drop
或take
到您的心(或要求)内容。好吧,我很确定你能。 Haven还没试过它:))
另外请注意。你必须处理打开zip流的函数里面的seq!
答案 3 :(得分:0)
Clojure-Contrib拥有图书馆IO和Jar,可缩短代码:
(require 'clojure.contrib.jar
'clojure.contrib.io)
(import [java.util.jar JarFile])
(defn- read-zip [zip-file]
(clojure.contrib.jar/filenames-in-jar (JarFile. (clojure.contrib.io/file zip-file))))
警告:函数filenames-in-jar不会列出zip文件中的目录条目,只列出实际文件的名称。