通过操纵行和列在ruby中重新格式化CSV



我必须使用一个csv文件,该文件不能直接用于生成简单图表的需要。我需要将文件操作成"更干净"的东西,并且遇到了问题,并且不确定我的总体策略是否正确,因为我刚刚学习使用ruby....解析文件我在这里的问题主要与我寻找的数据有关,这些数据与我找到或没有找到匹配的数据相抵消。在我找到符合标准的行之后,我需要从它之后的2行读取信息并操纵其中的一些(从最后一列移动到第二列)。

这是原始的csv文件:

component
quantity header,design1,design2,design3,Ref,Units
quantity type,#,#,#,ref#,unit value
component
quantity header,design1,design2,design3,Ref,Units
quantity type,#,#,#,ref#,unit value
component
quantity header,design1,design2,design3,Ref,Units
quantity type,#,#,#,ref#,unit value
所需输出:

Component Header,Quantity type Header,Units Header,design1 header,design2 header,design3 header,Ref header
component,quantity type,unit value,#,#,#,n/a
component,quantity type,unit value,#,#,#,n/a
component,quantity type,unit value,#,#,#,n/a
component,quantity type,unit value,#,#,#,n/a
component,quantity type,unit value,#,#,#,n/a

我的ruby脚本:

require 'csv'
f = File.new("sp.csv")
o = CSV.open('output.csv', 'w')
f.each_line do |l| #iterate through each line
    data = l.split
    if l !~ /,/ #if the line does not contain a comma it is a component
        o << [data,f.gets] #start writing data, f.gets skips next line but need to skip 2 and split the line to manipulate columns
    else
        o << ['comma'] #just me testing that I can find lines with commas
    end
end

f。Gets跳过下一行,文档对我来说不清楚如何使用它跳过2。在那之后,我想我可以用逗号分割那行,并用数组[列]操纵行数据。除了这个偏移问题,我也不确定我的一般方法是否是一个好策略

编辑

下面是真实文件....中的一些行我将通过提供的答案,看看我是否能使其全部工作。我的想法是逐行读取和写入,而不是将整个文件转换为数组,然后读取和写入。我的想法是,当这些文件变大时,它们确实会变大,逐行处理会占用更少的内存。

谢谢你的帮助,我会解决问题,然后回复你。

DCB
Result Quantity,BL::BL,BL::BL_DCB-noHeat,DC1::DC1,DC2::DC2,noHS::noHS,20mmHS::20mmHS,Reference,Units
Avg Temperature,82.915,69.226,78.35,78.383,86.6,85.763,N/A,Celsius
RCB
Result Quantity,BL::BL,BL::BL_DCB-noHeat,DC1::DC1,DC2::DC2,noHS::noHS,20mmHS::20mmHS,Reference,Units
Avg Temperature,76.557,68.779,74.705,74.739,80.22,79.397,N/A,Celsius
Antenna
Result Quantity,BL::BL,BL::BL_DCB-noHeat,DC1::DC1,DC2::DC2,noHS::noHS,20mmHS::20mmHS,Reference,Units
Avg Temperature,69.988,65.045,69.203,69.238,73.567,72.777,N/A,Celsius
PCBA_fiberTray
Result Quantity,BL::BL,BL::BL_DCB-noHeat,DC1::DC1,DC2::DC2,noHS::noHS,20mmHS::20mmHS,Reference,Units
Avg Temperature,66.651,65.904,66.513,66.551,72.516,70.47,N/A,Celsius

编辑2

使用下面答案中的一些正则表达式,我开发了一个逐行解析策略。为了完整,我会把答案贴出来。

谢谢你的帮助,让我了解了开发解决方案的方法

如何将其分成3行一组:

File.read("sp.csv").split("n").each_slice(3) do |slice|
  o << [slice[0], *slice[2].split(',')]
end

我基于示例创建了一个CSV文件,名为"test.csv"。

从下面的代码开始:

data = File.readlines('test.csv').slice_before(/^component/)
我得到一个枚举数。如果我看一下枚举器将返回的数据,我得到:
pp data.to_a
[["componentn",
  "quantity header,design1,design2,design3,Ref,Unitsn",
  "quantity type,#,#,#,ref#,unit valuen"],
["componentn",
  "quantity header,design1,design2,design3,Ref,Unitsn",
  "quantity type,#,#,#,ref#,unit valuen"],
["componentn",
  "quantity header,design1,design2,design3,Ref,Unitsn",
  "quantity type,#,#,#,ref#,unit valuen"]]

这是一个数组的数组,在"组件"行被分解成子数组。我怀疑这些值不能反映现实,但没有更准确的样本……GIGO。

如果"component"行不是一堆重复的"component"行,并且没有任何逗号,你可以用这个代替:

data = File.readlines('test.csv').slice_before(/A[^,]+Z/)

或:

data = File.readlines('test.csv').slice_before(/^[^,]+$/)

结果将与当前样品相同。

如果您需要更复杂的正则表达式,您可以替换它,例如:

/^(?:#{ Regexp.union(%w[component1 component2]).source })$/i

返回一个模式,该模式将查找%w[]数组中的任何单词:

/^(?:component1|component2)$/i

从那里,我们可以遍历data数组并使用

清除所有无关的标头:
data.map{ |a| a[2..-1] }.flatten

返回如下内容:

[
  "quantity type,#,#,#,ref#,unit valuen",
  "quantity type,#,#,#,ref#,unit valuen",
  "quantity type,#,#,#,ref#,unit valuen"
]

可以迭代并传递给CSV,以便在需要时解析为数组:

data.map{ |a| a[2..-1].map{ |r| CSV.parse(r) }.flatten }
[
  ["quantity type", "#", "#", "#", "ref#", "unit value"],
  ["quantity type", "#", "#", "#", "ref#", "unit value"],
  ["quantity type", "#", "#", "#", "ref#", "unit value"]
]

以上就是让您思考如何分解CSV数据的所有背景知识。

使用此代码:

data.flat_map { |ary|
  component = ary[0].strip
  ary[2..-1].map{ |a|
    data = CSV.parse(a).flatten
    [
      component,
      data.shift,
      data.pop,
      *data[0..-2]
    ]
  }
}

的回报:

[
  ["component", "quantity type", "unit value", "#", "#", "#"],
  ["component", "quantity type", "unit value", "#", "#", "#"],
  ["component", "quantity type", "unit value", "#", "#", "#"]
]

剩下唯一要做的就是创建您想要使用的头,并将返回的数据传递回CSV,让它生成输出文件。您应该能够使用CSV文档从这里到达那里。


编辑:

根据实际数据,这里有一个稍微调整的代码版本,以及它的输出:

require 'csv'
require 'pp'
data = File.readlines('test.csv').slice_before(/^[^,]+$/)
pp data.flat_map { |ary|
  component = ary[0].strip
  ary[2..-1].map{ |a|
    record = CSV.parse(a).flatten
    [
      component,
      record.shift,
      record.pop,
      *record[0..-2]
    ]
  }
}

看起来像:

[["DCB",
  "Avg Temperature",
  "Celsius",
  "82.915",
  "69.226",
  "78.35",
  "78.383",
  "86.6",
  "85.763"],
["RCB",
  "Avg Temperature",
  "Celsius",
  "76.557",
  "68.779",
  "74.705",
  "74.739",
  "80.22",
  "79.397"],
["Antenna",
  "Avg Temperature",
  "Celsius",
  "69.988",
  "65.045",
  "69.203",
  "69.238",
  "73.567",
  "72.777"],
["PCBA_fiberTray",
  "Avg Temperature",
  "Celsius",
  "66.651",
  "65.904",
  "66.513",
  "66.551",
  "72.516",
  "70.47"]]

代码,我使用它创建csv文件与一切操纵…感谢那些提供帮助的人。

require 'csv'
file_in = File.new('sp1.csv')
file_out = CSV.open('output.csv', 'w')
header = []
row = []

file_in.each_line do |line|
  case line
  when /^[^,]+$/ #Find a component (line with no comma)
    comp_header = file_in.gets.split(',') #header is after component and is split into an arry
    if header.empty? #header
      header.push("Component", comp_header[0], comp_header[-1].strip)
      comp_header[1..-3].each do |h|
        header.push(h)
      end
      file_out << header 
    end
    @comp = line.to_s.strip
    next
  when /,/ #when a row had commas
    puts @comp
    vals = line.split(',') #split up into vals array
    row.push(@comp, vals[0], vals[-1].strip) #add quantity and unit to row array
    vals[1..-3].each do |v| #for values (excluding quanity, units, reference info)
      row.push(v) #add values to row array
    end
  end
    file_out << row #write the current row to csv file
    row = [] #reset the row array to move on to the next component set
end

相关内容

  • 没有找到相关文章