使用Ruby查找大文件的最大CSV字段大小

时间:2013-07-23 09:09:07

标签: ruby csv

我正在尝试确定一个大型CSV文件(~5GB)的最大字段大小,其中包含300多个要转储到MySQL表中的字段。我对该文件的CSV文件架构给出了不正确的最大字段长度,因此我在表导入时遇到错误。我在Windows上运行Ruby 2.0。

我正在使用一个数组来根据字段的索引(或列)位置存储最大字段长度,即忽略标题中的字段实际名称。我尝试过使用哈希,注入和拉链等更好的东西,但似乎一个简单的数组在这里工作得最快。

 field_lengths[0] = Max length of first field   
 field_lengths[1] = Max length of second field
 etc.

文件太大而无法立即啜食或使用CSV按列进行解析。因此,我打开CSV文件并使用CSV#foreach来解析每一行(使用:headers => true选项忽略标题)。对于每一行,我遍历解析的字段值数组,并将字段的长度与存储在field_length数组中的当前最大长度进行比较。我意识到使用较小的文件有更简单的方法。此方法适用于较大的文件,但我仍然无法使用此方法将其放到我的特定文件的末尾。

为了避免无法完成文件,我当前定义了许多行,包括标题(= n),并在我到达第n行后中断。在下面的示例中,我从CSV文件中读取了101行。 (1个标题行+ 100个实际数据行)。我不确定文件中有多少总行数,因为该过程尚未完成。

 require 'csv'
 require 'pp'

 data_file = 'big_file.csv'

 # We're only reading the first 101 lines in this example
 n = 101
 field_lengths = []

 File.open(data_file) do |f|
   CSV.foreach(f, :headers => true, :header_converters => :symbol) do |csv_row|
       break if $. > n
       csv_row.fields.each_with_index do |a,i|
           field_lengths[i] ||= a.to_s.length
           field_lengths[i] = a.to_s.length if field_lengths[i] < a.to_s.length
       end
   end
 end

 pp field_lengths

IO #read可以读取一定数量的字节,但是如果我按字节解析文件,则记录可能会被拆分。有没有人有解析CSV文件的替代建议,将其拆分成较小的文件? O'Reilly的Ruby Cookbook(Lucas Carlson&amp; Leonard Richardson,2006,第1版)建议将一个大文件分成几个块(如下所示),但我不知道如何将它扩展到这个例子,特别是处理该行休息等。

 class File 
    def each_chunk(chunk_size = 1024)
        yield read(chunk_size) until eof?
    end
 end

 open("bigfile.txt") do |f|
    f.each_chunk(15) {|chunk| puts chunk}
 end

2 个答案:

答案 0 :(得分:2)

你使用CSV.foreach错误,它需要一个字符串作为文件名:

field_lengths = {}

CSV.foreach(data_file, :headers => true, :header_converters => :symbol) do |csv_row|
  csv_row.each do |k, v|
    field_lengths[k] = [field_lengths[k] || 0, v.length].max
  end
end

pp field_lengths

答案 1 :(得分:0)

在名为csv的变量中给出CSV :: Table,您可以将其更改为“by column”并使用collect或map ...或inject。有很多方法。

e.g。

"4.77 MB","20.xx.xx.2","10.xx.xx.1"
  • 第一张地图遍历列。
  • 第二个映射遍历标题(String),然后遍历行值数组。
  • 因此,第三个地图遍历行,您可以将值转换为字符串并返回其长度。
  • 第一个max是最大值字符串长度。
  • 第二个最大值是标题长度和最大值长度之间的最大值。
  • 最后,您可能希望将csv返回 col_or_row

e.g。

csv.by_col!
field_lengths = cols.map{|col| col.map{|r| r.is_a?(String) ? r.to_s.length : r.map{|v| v.to_s.length}.max }.max}