【发布时间】: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 \;)))
我确实通过反复试验得到了这个。关于这段代码,我主要想改变/理解三件事。
-
理想情况下,我想将我的代码分成两部分:一是下载并解压缩内容,然后返回一个流 - 原因是我想稍后决定是直接将其作为 csv 读取,还是写入到磁盘(我不想失去这个选项,因为在开发过程中,读取预下载的 csv 文件比每次下载大内容要容易得多)。事实证明,如果我尝试在
with-open调用之外访问流,我会收到“流已关闭”错误(据我了解,这是完全有道理的)。 -
在上面的代码中,我必须调用它
.getNextEntry,否则我会得到一个空列表。作为一个努力编写函数式代码的人,这让我很困扰,因为据我所知,我在这里处理的是状态——我的流对象看起来是可变的,这是我真的不想要的。有没有办法绕过这一步而直接没有它? -
我尝试直接在
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 点 - 也就是说,返回一个流并稍后决定如何处理它(也许就像在一个惰性流中,如果这有任何意义) ...