将标题放回我的CSV文件时遇到问题

时间:2016-12-01 21:53:36

标签: arrays ruby file csv

这是我的代码:

require 'CSV'

contents = CSV.read('/Users/namename/Desktop/test.csv')
arr = []
first_row = contents[0]
contents.shift

contents.each do |row|
  if row[12].to_s =~ /PO Box/i or row[12].to_s =~ /^[[:digit:]]/
    #File.open('out.csv','a').puts('"'+row.join('","')+'"')
    arr << row
  else
    row[12], row[13] = row[13], row[12]
    #File.open('out.csv','a').puts('"'+row.join('","')+'"')
    arr << row
  end
end

arr.unshift(first_row)

arr.each do |row|
  File.open('out.csv', 'a').puts('"' + row.join('","') + '"')
end

首先我.shift,以便我的标题字段不会在第一个.each循环的第一个条件中捕获模式(并最终交换)。然后我有条件地交换与模式匹配的单元格值,然后将正确移位的值存储在数组中。在此之后,我.unshift尝试放回我存储在first_row中的标题字段,但是当我查看生成的out.csv文件时,我将所有标题放在中间。为什么呢?

示例数据:

https://gist.github.com/anonymous/e1017d3ba81634d9e1227e7fe49536cb

1 个答案:

答案 0 :(得分:3)

问题的根源在于您没有使用CSV模块提供的功能。

首先,CSV.read采用:headers选项,可以为您捕获标题,因此您不必担心它们,并且作为奖励,您可以按标题名称访问字段而不是数字索引(如果CSV字段&#39;顺序更改,则很方便)。使用:headers选项,CSV.read会返回一个CSV :: Table对象,这会让我稍后讨论另一个好处。

其次,您正在生成自己的虚拟CSV输出,而不是让CSV模块执行此操作。特别是这是不必要和危险的:

...puts('"' + row.join('","') + '"')

如果您的任何列值包含需要转义的引号或换行符,则会失败,这很糟糕。您可以使用CSV.generate_line(row),但如果您使用上面的headers:选项,则不需要。就像我说的,它返回一个CSV::Table对象,它有一个to_csv方法,该方法接受:force_quotes选项。这将引用每个领域,就像你想要的那样 - 更重要的是,安全。

有了上述知识,代码变得更加健全:

require "csv"

contents = CSV.read('/Users/namename/Desktop/test.csv', headers: true)

contents.each do |row|
  next unless row["DetailActiveAddressLine1"] =~ /PO Box|^[[:digit:]]/i

  row["DetailActiveAddressLine1"], row["DetailActiveAddressLine2"] =
    row["DetailActiveAddressLine2"], row["DetailActiveAddressLine1"]
end

File.open('out.csv', 'a') do |file|
  file.write(contents.to_csv(force_quotes: true))
end

如果您愿意,可以在Ideone上看到正在运行的代码版本(当然没有文件访问权限):http://ideone.com/IkdCpb