我遇到问题,需要下载、解压缩,然后逐行处理一个非常大的 CSV 文件。我认为让您了解文件有多大很有用:
- big_file.zip ~700mb
- big_file.csv ~23gb
这是我希望发生的一些事情:
- 解压前不必下载整个文件
- 在解析 csv 行之前不必解压整个文件
- 执行所有这些操作时不要消耗太多内存/磁盘
我不知道这是否可能。这就是我的想法:
require 'open-uri'
require 'rubyzip'
require 'csv'
open('http://foo.bar/big_file.zip') do |zipped|
Zip::InputStream.open(zipped) do |unzipped|
sleep 10 until entry = unzipped.get_next_entry && entry.name == 'big_file.csv'
CSV.foreach(unzipped) do |row|
# process the row, maybe write out to STDOUT or some file
end
end
end
以下是我所知道的问题:
-
open-uri
读取整个响应并将其保存到Tempfile
对于这种大小的文件来说这不太好。我可能需要使用Net::HTTP
直接但我不知道如何做到这一点并且仍然得到IO
.
- 我不知道下载速度有多快,也不知道是否可以
Zip::InputStream
按照我展示的方式工作。当文件尚未全部存在时,它可以解压部分文件吗?
- 会不会
CSV.foreach
使用 rubyzip 的InputStream
?它的行为是否足够像File
它能够解析出行吗?如果它想读取但缓冲区是空的,它会惊慌吗?
我不知道这是否是正确的方法。也许某些 EventMachine 解决方案会更好(虽然我以前从未使用过 EventMachine,但如果它对于这样的事情效果更好,我完全赞成)。
自从我发布这个问题以来已经有一段时间了,如果其他人遇到这个问题,我认为可能值得分享我的发现。
- 对于行数,我正在处理 Ruby 的标准库
CSV
太慢了。我的 csv 文件非常简单,我不需要所有这些东西来处理引用的字符串或类型强制。只需使用就容易多了IO#gets
然后用逗号分隔该行。
- 我无法将整个内容从 http 传输到
Zip::Inputstream
对某些人IO
包含 csv 数据。这是因为zip 文件结构 https://en.wikipedia.org/wiki/Zip_(file_format)#Structure文件末尾有中央目录结尾 (EOCD)。这是提取文件所必需的,因此从 http 流式传输它似乎不起作用。
我最终采用的解决方案是将文件下载到磁盘,然后使用 Ruby 的 open3 库和 Linuxunzip
包以流式传输 zip 中未压缩的 csv 文件。
require 'open3'
IO.popen('unzip -p /path/to/big_file.zip big_file.csv', 'rb') do |io|
line = io.gets
# do stuff to process the CSV line
end
The -p
打开 unzip 将提取的文件发送到 stdout。IO.popen
然后使用管道使其成为IO
红宝石中的对象。效果非常好。你可以将它与CSV
如果你想要额外的处理,那对我来说太慢了。
require 'open3'
require 'csv'
IO.popen('unzip -p /path/to/big_file.zip big_file.csv', 'rb') do |io|
CSV.foreach(io) do |row|
# process the row
end
end
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)