这是我的代码:
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
答案 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