我在txt文件中有一个示例数据集。数据文件非常大,因此无法将其加载到内存中。我需要能够懒惰地读取文件。此外,我需要以随机顺序读取行。并且可能存在我不需要阅读所有行的情况。这是我到目前为止所发现的 -
(defn read-lazy [in-file]
(letfn [(helper [rdr]
(if-let [line (.readLine rdr)]
(cons line (helper rdr))
(do (.close rdr) nil)))]
(helper (io/reader in-file))))
返回文件的lazy-seq。当我需要时,如何在lazy-seq中循环遍历随机行?我认为使用go块可以帮到这里。 Go块可以在通道中放置一个随机行,并等待消耗它。一旦数据被读取,它就会在通道中放入另一行,等待下一次读取。我该如何实现呢?
这是我如何解决的问题(非随机) -
(def lazy-ch (chan))
(defn async-fetch-set [in-file]
(go
(with-open [reader (io/reader in-file)]
(doseq [line (line-seq reader)]
(>! lazy-ch line)))
(close! lazy-ch)))
(println "got: " (<!! lazy-ch))
这是解决问题的好方法吗?有更好的解决方案吗?我可能不需要阅读所有的内容,因此我希望能够在需要的时候关闭阅读器。
答案 0 :(得分:1)
您的上述解决方案不包含任何随机性。 Go频道是先进先出的结构。如果你真的想要随机读取,首先需要计算文件中的行数,然后使用I
在区间[0..N-1]中生成一个整数I
,然后读取文件中的{{1}}行。
有几种不同的方法可以从文件中读取行{{1}},这可以权衡速度与内存要求。
答案 1 :(得分:1)
(defn char-seq
"Returns a lazy sequence of characters from rdr. rdr must implement
java.io.Reader."
[rdr]
(let [c (.read rdr)]
(if-not (neg? c)
(cons (char c) (lazy-seq (char-seq rdr))))))
(defn line-offsets
"Returns a lazy sequence of offsets of all lines in s."
[s]
(if (seq s)
(->> (partition-all 3 1 s)
(map-indexed
(fn [i [a b c]]
(cond
(= b \newline) (if c (+ 2 i))
(= a \return) (if b (inc i)))))
(filter identity)
(cons 0))))
(defn ordered-line-seq
"Returns the lines of text from raf at each offset in offsets as a lazy
sequence of strings. raf must implement java.io.RandomAccessFile."
[raf offsets]
(map (fn [i]
(.seek raf i)
(.readLine raf))
offsets))
使用示例:
(let [filename "data.txt"
offsets (with-open [rdr (clojure.java.io/reader filename)]
(shuffle (line-offsets (char-seq rdr))))]
(with-open [raf (java.io.RandomAccessFile. filename "r")]
(dorun (map println (ordered-line-seq raf offsets)))))