3

我正在处理一个很大的 Subrip 字幕文件,需要一次处理一个字幕。在 Java 中,要从文件中提取字幕,我会编写一个具有以下签名的方法:

Iterator<Subtitle> fromSubrip(final Iterator<String> lines);

的使用Iterator给了我两个好处:

  1. 该文件永远不会完整地存储在内存中,它的任何转换阶段也不会。
  2. Subtitle一种抽象,我可以在没有内存开销的情况下遍历对象集合。

由于迭代器本质上是命令式和可变的,因此它们在 Clojure 中可能不是惯用的。那么处理这种情况的 Clojure 方法是什么?

4

3 回答 3

3

正如弗拉基米尔所说,您需要正确处理懒惰和文件关闭。下面是我的做法,如“ Read a very large text file into a list in clojure ”中所示:

(defn lazy-file-lines 
  "open a (probably large) file and make it a available as a lazy seq of lines"
  [filename]
  (letfn [(helper [rdr]
                  (lazy-seq
                    (if-let [line (.readLine rdr)]
                      (cons line (helper rdr))
                      (do (.close rdr) nil))))]
         (helper (clojure.java.io/reader filename))))
于 2013-04-14T15:30:13.320 回答
2

从目录中读取所有文件,这是一种懒惰的方式。

使用变黑和通道。

代码:

(ns user
  (:require [clojure.core.async :as async :refer :all 
:exclude [map into reduce merge partition partition-by take]]))

(defn read-dir [dir]
  (let [directory (clojure.java.io/file dir)
        files (filter #(.isFile %) (file-seq directory))
        ch (chan)]
    (go
      (doseq [file files]
        (with-open [rdr (clojure.java.io/reader file)]
          (doseq [line (line-seq rdr)]
            (>! ch line))))
      (close! ch))
    ch))

调用:

(def aa "D:\\Users\\input")
(let [ch (read-dir aa)]
  (loop []
    (when-let [line (<!! ch )]
      (println line)
      (recur))))

=================

reify Iterable 接口,可以在java中使用。

MyFiles.clj:
(ns user
  (:gen-class :methods [#^{:static true} [readDir [String] Iterable]])
  (:require [clojure.core.async :as async :refer :all 
:exclude [map into reduce merge partition partition-by take]]))

(defn -readDir [dir]
  (def i nil)
  (let [ch (read-dir dir)
        it (reify java.util.Iterator
             (hasNext [this] (alter-var-root #'i (fn [_] (<!! ch))) (not (nil? i)))
             (next [this] i))
        itab (reify Iterable
               (iterator [this] it))]
    itab))

爪哇代码:

for (Object line : MyFiles.readDir("/dir")) {
    println(line)
}
于 2014-11-20T14:43:19.717 回答
1

您可以为此使用惰性序列,例如line-seq.

但是,您必须小心,由line-seq(以及基于某些外部资源返回惰性序列的其他函数)返回的序列永远不会泄漏到例如with-open范围之外,因为在关闭源之后,进一步读取惰性序列将导致异常。

于 2013-04-14T05:42:33.083 回答