使用clojure中的java api读取zip文件

时间:2011-03-24 12:31:42

标签: java clojure

我正在尝试在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)))

4 个答案:

答案 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,您可以filterdroptake到您的心(或要求)内容。好吧,我很确定你能。 Haven还没试过它:))

另外请注意。你必须处理打开zip流的函数里面的seq!

答案 3 :(得分:0)

Clojure-Contrib拥有图书馆IOJar,可缩短代码:

(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文件中的目录条目,只列出实际文件的名称。