Я работаю над сценарием, который должен прочитать десятки тысяч файлов с диска. Я пытаюсь понять лучший способ сделать это. Я столкнулся с проблемой, когда я использую map
, чтобы сделать это, используя два пакета clj-glob
и clojure-mail
:
(def sent-mail-paths
(->> (str maildir-path "/*/_sent_mail/*")
(glob) ;; returns files using clojure.java.io/as-file
(map str) ;; i just want the paths
))
(def msgs
(->> sent-mail-paths ;; 30K + paths
(map mail/file->message)))
, где функция glob
в первом блоке взята из clj-glob
и использует as-file
для возврата набора файловых объектов (см. здесь ). Я хочу только строки пути, поэтому я делаю (map str)
. Функция mail/file->message
во втором блоке использует with-open
вместе с классом java FileInputStream
для чтения файлов (см. здесь ).
Проблема, с которой я сталкиваюсь, заключается в том, что этот код вызывает ошибку в тот момент, когда я пытаюсь обработать файлы в получающейся ленивой последовательности, выполняя даже что-то вроде:
(count msgs)
Ошибка:
(Слишком много открытых файлов в системе)
Единственный способ выполнить эту работу - использовать doseq
:
(def msgs (->> list-of-paths ;; 30K+ paths
(map mail/file->message)))
(def final (atom []))
(doseq [x result]
(swap! final conj (mail/file->message x)))
Мой вопрос заключается в том, является ли это лучшим (единственным?) Способом выполнить этот процесс, не открывая одновременно тысячи и тысячи файлов? Я не до конца понимаю, почему я не могу использовать ленивую последовательность, возвращаемую map
. Почему это в конечном итоге открывает тонны файлов.
Кстати, одна вещь, которую я заметил, это то, что clj-glob
, который не является исправным пакетом, не использует with-open
, когда он вызывает as-file
...