【问题标题】:How to map and edit a CSV file with Ruby如何使用 Ruby 映射和编辑 CSV 文件
【发布时间】:2015-10-24 18:27:36
【问题描述】:

有没有办法在 Ruby 中使用 map 方法编辑 CSV 文件?我知道我可以使用以下方法打开文件:

CSV.open("file.csv", "a+")

并向其中添加内容,但我必须编辑一些特定的行。

foreach 方法仅对读取文件有用(如果我错了,请纠正我)。

我检查了 Ruby CSV documentation,但找不到任何有用的信息。

我的 CSV 文件少于 1500 行,所以我不介意阅读所有行。

【问题讨论】:

  • 虽然您可以使用“'a+'”,但更好的问题是您应该这样做吗?如果您的代码在运行中崩溃,您很有可能会损坏您唯一的文件,这在生产环境。改为使用foreach读取原始文件并开始写入一个全新的文件,在适当的地方添加更改。新文件完全写入后,将旧文件重命名为备份文件,将新文件重命名为旧文件的名称,然后然后您可以随意删除旧文件。不要乱用原始文件,因为它速度较慢且不可扩展。
  • 以下是否回答了您的问题@Badoo
  • 请阅读“minimal reproducible example”。我们想知道您尝试解决此问题的方法,以及为什么它不起作用。没有它,您似乎没有尝试并希望我们为您编写代码,这不是 SO 方式。见meta.stackoverflow.com/q/274630/128421meta.stackoverflow.com/questions/261592
  • 你没有说你如何确定哪些行必须改变。按行号?按内容排列?一般来说,不,map 不是一个好的选择,因为它意味着每一行都必须通过该块。相反,一个简单的条件测试遍历行并寻找需要更改的行会更明智。

标签: ruby csv


【解决方案1】:

使用each.with_index()的另一个答案:

rows_array = CSV.read('sample.csv')

desired_indices = [3, 4, 5].sort # these are rows you would like to modify
rows_array.each.with_index(desired_indices[0]) do |row, index| 
  if desired_indices.include?(index)

    # modify over here
    rows_array[index][target_column] = 'modification'

  end
end

# now update the file
CSV.open('sample3.csv', 'wb') { |csv| rows_array.each{|row| csv << row}}

您也可以使用each_with_index {} insead of each.with_index {}

【讨论】:

  • 这会破坏数据,这不是一个可扩展的解决方案,因此只有在保证文件永远不会超过可用内存的情况下才这样做。见stackoverflow.com/q/25189262/128421
【解决方案2】:

有没有办法在 Ruby 中使用 map 方法编辑 CSV 文件?

是的:

rows = CSV.open('sample.csv')
rows_array = rows.to_a

rows_array = CSV.read('sample.csv')

desired_indices = [3, 4, 5] # these are rows you would like to modify

edited_rows = rows_array.each_with_index.map do |row, index| 
  if desired_indices.include?(index)

    # simply return the row
    #   or modify over here
    row[3] = 'shiva'

    # store index in each edited rows to keep track of the rows
    [index, row]

  end
end.compact

# update the main row_array with updated data
edited_rows.each{|row| rows_array[row[0]] = row[1]}

# now update the file
CSV.open('sample2.csv', 'wb') { |csv| rows_array.each{|row| csv << row}}

这有点混乱。是不是?我建议您使用 each_with_index 而不使用 map 来执行此操作。看我的另一个答案

【讨论】:

  • 小心使用这种技术,因为它会将整个文件吞入内存。除非保证输入永远不会超过内存,否则逐行解决方案会更安全、更快。见stackoverflow.com/q/25189262/128421
【解决方案3】:

这是我编写的一个小脚本作为示例,说明如何读取 CSV 数据,对数据执行操作,然后将编辑后的文本写入新文件:

read_write_csv.rb:

#!/usr/bin/env ruby
require 'csv'

src_dir = "/home/user/Desktop/csvfile/FL_insurance_sample.csv"
dst_dir = "/home/user/Desktop/csvfile/FL_insurance_sample_out.csv"
puts " Reading data from  : #{src_dir}"
puts " Writing data to    : #{dst_dir}"
#create a new file 
csv_out = File.open(dst_dir, 'wb')
#read from existing file
CSV.foreach(src_dir , :headers => false) do |row|

  #then you can do this 
  # newrow = row.each_with_index { |rowcontent , row_num| puts "#     {rowcontent} #{row_num}" }

  # OR array to hash .. just saying .. maybe hash of arrays.. 
  #h = Hash[*row]
  #csv_out << h

  # OR use map  
  #newrow = row.map(&:capitalize)
  #csv_out << h

  #OR use each  ... Add and end 
  #newrow.each do |k,v| puts "#{k} is #{v}"

  #Lastly,  write back the edited , regexed data ..etc to an out file.
  #csv_out << newrow

end

# close the file 
csv_out.close

输出文件有所需的数据:

USER@USER-SVE1411EGXB:~/Desktop/csvfile$ ls
FL_insurance_sample.csv  FL_insurance_sample_out.csv  read_write_csv.rb

输入文件数据如下所示:

policyID,statecode,county,eq_site_limit,hu_site_limit,fl_site_limit,fr_site_limit,tiv_2011,tiv_2012,eq_site_deductible,hu_site_deductible,fl_site_deductible,fr_site_deductible,point_latitude,point_longitude,line,construction,point_granularity
119736,FL,CLAY COUNTY,498960,498960,498960,498960,498960,792148.9,0,9979.2,0,0,30.102261,-81.711777,Residential,Masonry,1
448094,FL,CLAY COUNTY,1322376.3,1322376.3,1322376.3,1322376.3,1322376.3,1438163.57,0,0,0,0,30.063936,-81.707664,Residential,Masonry,3
206893,FL,CLAY COUNTY,190724.4,190724.4,190724.4,190724.4,190724.4,192476.78,0,0,0,0,30.089579,-81.700455,Residential,Wood,1
333743,FL,CLAY COUNTY,0,79520.76,0,0,79520.76,86854.48,0,0,0,0,30.063236,-81.707703,Residential,Wood,3
172534,FL,CLAY COUNTY,0,254281.5,0,254281.5,254281.5,246144.49,0,0,0,0,30.060614,-81.702675,Residential,Wood,1

【讨论】:

  • 感谢@the Tin Man 修改此内容。铁皮人就是男人。
  • 你应该使用File.open(dst_dir, 'wb')的块形式而不是分配给一个变量。这是 Ruby 的方式。
  • 我觉得有一个错误:csv_out = File.open(dst_dir, 'wb')应该是csv_out = CSV.open(dst_dir, 'wb'),否则写成单行对应不同元素的串联。
猜你喜欢
  • 1970-01-01
  • 2015-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多