Чтение ZIP-файла с использованием Java API от Clojure - PullRequest
8 голосов
/ 24 марта 2011

Я пытаюсь переписать следующий фрагмент в ближайшем будущем, но все получается безобразно, может быть, кто-то предложит более элегантное решение?

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 ]

17 голосов
/ 24 марта 2011

Я бы сказал что-то вроде следующего:

(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))))

РЕДАКТИРОВАТЬ: вышеуказанный код был в конечном итоге проверен и исправлен.

РЕДАКТИРОВАТЬ: следующее работает, как ожидалось, и это гораздо более кратким, хотя он использует другой API Java

(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)))))
6 голосов
/ 25 марта 2011

Это более простой пример:

(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"))

и это даст вам хороший список.

1 голос
/ 04 ноября 2014

Это похоже на ответ Скуро, который использует ZipInputStream, но немного более краткое определение 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. Прелесть этого в том, что, поскольку записи в ленивом порядке, вы можете filter или drop или take для вашего сердца (или требования) контента. Ну, я уверен, что вы можете. Еще не пробовал:)

Также обратите внимание. Вы ДОЛЖНЫ обработать последовательность внутри функции, в которой вы открываете поток zip !!!

0 голосов
/ 24 марта 2011

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-файле, только имена реальных файлов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...