使用foreach函数从csv文件中读取数据

时间:2017-02-18 19:10:53

标签: ruby-on-rails ruby

我一直在读取来自csv的数据,如果有一个大的csv文件,为了避免这个超时(机架12秒超时)我只读了25行之后来自csv的25行它再次发出请求所以这将继续,直到读完所有行。

def read_csv(offset)
  r_count = 1
  CSV.foreach(file.tempfile, options) do |row|
  if r_count > offset.to_i
    #process
  end
  r_count += 1
end

但是这里它正在创建一个新问题,让我们首先读取25行,然后当下一个请求偏移时为25,那时它将读取前25行,然后它将从26开始读取并进行处理,那怎么能我跳过已经读过的行?,我试过这个如果下一个跳过迭代但是失败了,还是有其他有效的方法可以做到这一点?

2 个答案:

答案 0 :(得分:0)

<强>代码

Error (active)      
function "scalePoint" (declared at line 13) was previously not declared constexpr

纯Ruby - SLOWER

def read_csv(fileName)
  lines = (`wc -l #{fileName}`).to_i + 1
  lines_processed = 0
  open(fileName) do |csv|
    csv.each_line do |line|
      #process
      lines_processed += 1
    end
  end
end

<强>基准

我运行了一个新的基准,比较了您提供的原始方法和我自己的方法。我还包括了测试文件信息。

def read_csv(fileName)
  lines = open("sample.csv").count
  lines_processed = 0
  open(fileName) do |csv|
    csv.each_line do |line|
      #process
      lines_processed += 1
    end
  end
end

<强>解释

注意:我添加了一个纯ruby方法,因为使用"File Information" Lines: 1172319 Size: 126M "django's original method" Time: 18.58 secs Memory: 0.45 MB "OneNeptune's method" Time: 0.58 secs Memory: 2.18 MB "Pure Ruby method" Time: 0.96 Memory: 2.06 MB 有点作弊,而且不可移植。在大多数情况下,使用纯语言解决方案非常重要。

您可以使用此方法处理非常大的CSV文件。

~2MB内存我觉得考虑到文件大小是非常优化的,它有点增加了内存使用量,但节省的时间似乎是一个公平的交易,这将防止超时。

我确实修改了采用fileName的方法,但这只是因为我测试了许多不同的CSV文件以确保它们都能正常工作。如果您愿意,可以删除它,但它可能会有所帮助。

我还删除了偏移的概念,因为您声明最初包含它以尝试自己优化解析,但这不再是必需的。

此外,我会跟踪文件中有多少行,以及自您需要使用该信息以来处理了多少行。注意,这些行只适用于基于unix的系统,并且它是一个避免将整个文件加载到内存中的技巧,它计算新行,并且我为最后一行添加1。如果您不打算将标题计为行,则可以删除+1并将行更改为“行”以更准确。

您可能遇到的另一个后勤问题是,如果CSV文件有标题,需要弄清楚如何处理。

答案 1 :(得分:-2)

你可以使用延迟读取来加快速度,整个文件都不会被读取,只是从文件的开头直到你使用的块。 有关示例,请参阅http://engineering.continuity.net/csv-tricks/https://reinteractive.com/posts/154-improving-csv-processing-code-with-laziness

您还可以使用SmarterCSV来处理这样的块。

SmarterCSV.process(file_path, {:chunk_size => 1000}) do |chunk|
  chunk.each do |row|
    # Do your processing
  end
  do_something_else
end
enter code here

我这样做的方法是将结果传输给用户,如果你看到发生了什么事,那就不必费心了。你提到的超时不会发生在这里。 我不是Rails用户,所以我举一个Sinatra的例子,这也可以用Rails完成。参见例如http://api.rubyonrails.org/classes/ActionController/Streaming.html

require 'sinatra'

get '/' do
  line = 0
  stream :keep_open do |out|
    1.upto(100) do |line| # this would be your CSV file opened
      out << "processing line #{line}<br>"
      # process line
      sleep 1 # for simulating the delay
    end
  end
end

更好但有点复杂的解决方案是使用websockets,一旦处理完成,浏览器就会从服务器接收结果。您还需要在客户端中使用一些javascript来处理这个问题。见https://github.com/websocket-rails/websocket-rails