【问题标题】:Need help downloading and reading a zipped CSV file in memory with Clojure需要帮助使用 Clojure 下载和读取内存中的压缩 CSV 文件
【发布时间】:2020-10-01 17:09:30
【问题描述】:

我有一个外部站点,我想从该站点下载压缩的 CSV 文件。目前,我正在下载它解压缩,将其保存到磁盘,然后解压缩,将解压缩的文件保存到磁盘,然后使用 CSV 阅读器读取解压缩的文件。流程中很多无用的步骤都可以删减掉,我就按照自己的方式去做了。

This amazing answer 帮助我继续前进。我尝试使用那里链接的第一个选项 (GZIPInputStream),但我收到“非 GZIP 格式”错误,所以我想我必须转到第二个选项。

这是我当前的代码,它做我想做的事:

(defn download-zipped-stream!
  (:body (clj-http.client/get "www.example.com" {:as :stream})))

(with-open
  [stream (ZipInputStream. download-zipped-stream!)]
  (.getNextEntry stream)
  (doall (clojure.data.csv/read-csv (clojure.java.io/reader stream) :separator \;)))

我确实通过反复试验得到了这个。关于这段代码,我主要想改变/理解三件事。

  1. 理想情况下,我想将我的代码分成两部分:一是下载并解压缩内容,然后返回一个流 - 原因是我想稍后决定是直接将其作为 csv 读取,还是写入到磁盘(我不想失去这个选项,因为在开发过程中,读取预下载的 csv 文件比每次下载大内容要容易得多)。事实证明,如果我尝试在 with-open 调用之外访问流,我会收到“流已关闭”错误(据我了解,这是完全有道理的)。

  2. 在上面的代码中,我必须调用它.getNextEntry,否则我会得到一个空列表。作为一个努力编写函数式代码的人,这让我很困扰,因为据我所知,我在这里处理的是状态——我的流对象看起来是可变的,这是我真的不想要的。有没有办法绕过这一步而直接没有它?

  3. 我尝试直接在stream 对象上调用read-csv 方法,但read-csv 显然并不真正知道如何处理ZipInputStreams。看到这一点,我简单而希望地在两者之间打了一个io/reader 电话,它奏效了。不过,我不知道这是否是最好的方法。对吗?

我对 Clojure 很陌生,而且我对 Java 完全一无所知,因此,正如您所见,我对这些流对象的了解非常有限。我尝试在 Java 中阅读有关它的一些内容,但我放弃了,因为我不确定其中有多少对学习 Clojure 的人有用,因此也感谢任何指针。

【问题讨论】:

  • 你不能为 2) 做任何事情,除了隐藏它或寻找一个库,它会为你隐藏 - 这就是 java 类的实现方式。如果您想更好地“隐藏”它,您可以执行(-> stream (doto (.getNextEntry)) (clojure.java.io/reader) (clojure.data.csv/read-csv) (doall))) 之类的操作,这使其看起来更像是“流水线”,而不仅仅是某些行之间的命令式调用。但是我不确定,您在这里提出的问题是 - 这看起来很像代码审查请求?
  • 嗯,2. 和 3. 是审查式的,我同意(尽管我并不是想弄清楚这段代码是否好,而是想知道概念是否正确,或者如果有更好的选择),但我仍然不知道如何完成第 1 点 - 也就是说,返回一个流并稍后决定如何处理它(也许就像在一个惰性流中,如果这有任何意义) ...

标签: csv clojure


【解决方案1】:

我认为您的方法是正确的。建议考虑:

  1. 考虑使用wget 手动将*.csv.gz 文件下载到本地磁盘。然后,只需打开该本地文件,而不是使用 clj-http.client/get

  2. ZipInputStream 我玩的不多,但如果似乎需要使用.getNextEntry(),那就去吧。

  3. read-csv 的示例显示使用 Reader 来授予对输入文件的访问权限,因此这是预期的行为。

  4. This template project 展示了我喜欢如何组织 Clojure 项目和源代码。请务必仔细阅读所提供的文档列表。

  5. 不要忘记utilize cljdoc.org 来查找 Clojure 库 API 文档。例如,请参阅the API docs for data.csv


更新

您可能还想查看this answer

【讨论】:

  • OP 希望摆脱中间工件和步骤,因此建议使用随机 shell 工具似乎是个坏建议。
  • 鉴于 OP 注释“读取预下载的 csv 文件”,我认为 wget 或类似的方法是为开发工作实现文件本地缓存的最佳方法。
  • 我明白了,但我一直在寻找与代码集成的东西,而不是一些实用程序(重点是避免在开发过程中手动检查)。为此,我最终构建了两个独立的函数,一个下载解压缩后保存到磁盘,另一个下载、解压缩并返回一个序列。不确定是否可以构建“惰性流”。
【解决方案2】:

https://github.com/techascent/tech.ml.dataset 可选地与https://scicloj.github.io/tablecloth/index.html 一起使用(用于 TMD 的 dplyr 之类的 api)

还具有速度极快并且能够处理无法放入内存的数据集的优势,如 SQL、Arrow 等。人。在这里加入关于它的对话: https://clojurians.zulipchat.com/#narrow/stream/151924-data-science/topic/tech.2Eml.2Edataset

【讨论】:

    猜你喜欢
    • 2019-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-27
    • 1970-01-01
    • 2020-12-01
    相关资源
    最近更新 更多