【问题标题】:Using Ruby to replace specific records in a CSV file with records from another CSV file使用 Ruby 将 CSV 文件中的特定记录替换为另一个 CSV 文件中的记录
【发布时间】:2012-12-28 09:19:56
【问题描述】:

我有两个大型 CSV 文件。一个文件只是一个记录列表。另一个文件是记录列表,但第一列是它在另一个文件中修改的记录的行号。它不会替换整行;它只是替换具有匹配标题的行中的值。

例如:

文件 1:

"First","Last","Lang"
"John","Doe","Ruby"
"Jane","Doe","Perl"
"Dane","Joe","Lisp"

文件 2:

"Seq","Lang"
2,"Ruby"

我们的目标是最终得到一个如下所示的文件:

"First","Last","Lang"
"John","Doe","Ruby"
"Jane","Doe","Ruby"
"Dane","Joe","Lisp"

然而,数据比这复杂得多,甚至可能在 CSV 中包含换行符。因此,我不能依赖行号,而必须依赖记录数。 (当然,除非我对这两个文件都进行了预处理以替换换行符和回车符......我认为这是可能的,但没那么有趣。)

我的问题是如何在不将整个文件中的任何一个加载到内存的情况下循环遍历这两个文件并进行正确的替换。我相信将 100mb+ 文件加载到内存中是个坏主意,对吧?

此外,完成后生成的文件中的记录应按相同顺序排列。

【问题讨论】:

  • 猜猜这些手工编码的 CSV 文件?我假设是因为您将每个字符串包装在 " 中,而对于您提供的值来说这完全没有必要。
  • 文件 2 的“Seq”列是否按顺序排列?
  • 我只是手动输入示例。在真实文件的情况下,除非必要,否则其中一个不使用引号,而另一个强制引用。 CSV 应该以任何一种方式解析它们,带或不带引号。
  • pguardiario:是的,它应该总是按顺序排列的。

标签: ruby shell csv fastercsv


【解决方案1】:

您将需要 2 个枚举器,但由于它们不是嵌套的,因此需要使用 Enumerator#next,这意味着您需要小心它引发 EOF 异常:

e = CSV.open('file2.csv', :headers => true).each
seq = e.next

output = CSV.open('output.csv', 'w')

csv = CSV.open('file1.csv')
csv.each do |row|
  if seq['Seq'].to_i == csv.lineno - 1
    row[2] = seq['Lang']
    seq = e.next rescue ({'Seq' => -1})
  end
  output << row
end

【讨论】:

    【解决方案2】:

    如果文件太大而无法加载到内存中,这基本上就是我的处理方式

    // pseudocode
    
    f1 = fopen(file1)
    f2 = fopen(file2)
    f3 = fopen(newfile)
    
    // loop through exceptions
    foreach row2, index2 of f2
    
      // loop through file1 until a matched row is found
      while (row1, index1 of f1) && (row1 not null) && (row2[seq] <= index1)
    
        // patch
        if row2[seq] == index1
          row1[lang] = row2[lang]
        endif
    
        // write out to new file
        f3.write row1
    
      endwhile
    endforeach
    

    † 由于您的file2 具有基于1 的索引(而不是基于0),因此您需要在1 开始您的index1index2 计数器。


    †† 如果lang 不是您将始终替换的列:

    // at the beginning of the foreach loop
    if col is null
      cols = array_keys row2
      col = cols[2] // 1-based index
    end
    
    // the new patch block
    if row2[seq] == index1
      row1[col] = row2[col]
    endif
    

    【讨论】:

      猜你喜欢
      • 2018-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多