【问题标题】:Write and read array data in CSV row在 CSV 行中写入和读取数组数据
【发布时间】:2019-09-12 13:54:04
【问题描述】:

我遇到了一个无法解决的问题:我需要编写一个 CSV 文件来处理 CSV 数据。我这样做是这样的:

payload = {name: 'Dimitrius', errors: ['error1'] }
CSV.open(file_name, "a+") do |csv|
  csv.add_row(payload.values)
end

CSV 文件的创建和填充就像一个魅力 \o/ 但是,CSV 文件中的errors 数组是一个“字符串”:

csv_text = File.read(file_name)
csv = CSV.parse(csv_text, headers: false)
puts csv # [["Dimitrius", "[\"error1\"]"]

问题是,我必须创建很多“变通办法”来将字符串"[\"error1\"]" 转换为有效数组:(

我的问题是,这是在 CSV 文件中处理数组值的正确方法吗?

【问题讨论】:

    标签: ruby csv


    【解决方案1】:

    我借鉴了 Dimitrius 使用保留字符表示数组的想法,并通过创建自定义数据转换器将包含保留字符的字符串转换为数组,然后将其元素转换为适当的数据类型,对此进行了扩展.

    数据

    arr = [{ name: 'Dimitrius', errors: ['e1', 3, 4.1] },
           { name: 'Wilma',     errors: ['f1', 7, 9.5] }]
    

    写入 CSV 文件

    require 'csv'
    
    FName = 't.csv'
    

    我假设如果 CSV 文件中逗号之间的子字符串包含指定的保留字符,则它表示一个数组,保留字符分隔数组的元素。

    RESERVED_CHAR = '|'
    
    keys = arr.first.keys
      #=> [:name, :errors] 
    rows = arr.map(&:values)
      #=> [["Dimitrius", ["e1", 3, 4.1]],
      #    ["Wilma",     ["f1", 7, 9.5]]]
    
    CSV.open(FName, "wb") do |csv|
      csv << keys
      rows.each { |name,arr| csv << [name, arr.join(RESERVED_CHAR)] }
    end
      #=> [["Dimitrius", ["e1", 3, 4.1]], ["Wilma", ["f1", 7, 9.5]]] 
    

    让我们看看写了什么。

    File.read(FName).each_line { |line| p line }
    "name,errors\n"
    "Dimitrius,e1|3|4.1\n"
    "Wilma,f1|7|9.5\n"
    

    读取 CSV 文件

    CSV 类有六个内置数据转换器(在“常量”下查看)。但是,可以根据需要添加额外的转换器。这些转换行中的值,但不转换标题。

    require 'csv'
    FName = 't.csv'
    RESERVED_CHAR = '|'
    
    CSV::Converters[:array] = ->(v) do
      if v.include?(RESERVED_CHAR)
        v.split(RESERVED_CHAR).map do |e|
          case e
          when /\A\-?\d+\z/
            e.to_i
          when /\A\-?\d+\.\d+\z/
            e.to_f
          else
            e
          end
        end
      else
        v
      end
    end
    

    我们现在可以将 CSV 文件读入 CSV::Table 对象:

    tbl = CSV.read(FName, headers: true, converters: :array)
      #=> #<CSV::Table mode:col_or_row row_count:3> 
    

    我们可以使用它来提取标题和行:

    headers, *body = tbl.to_a
      #=> [["name", "errors"],
      #    ["Dimitrius", ["e1", 3, 4.1]],
      #    ["Wilma", ["f1", 7, 9.5]]] 
    headers
      #=> ["name", "errors"] 
    body
      #=> [["Dimitrius", ["e1", 3, 4.1]],
      #    ["Wilma",     ["f1", 7, 9.5]]]
    

    或个别列:

    tbl["name"]
      #=> ["Dimitrius", "Wilma"] 
    tbl["errors"]
      #=> [["e1", 3, 4.1],
      #    ["f1", 7, 9.5]]
    

    请注意,数组中的值已根据需要进行了转换,并且标题现在是字符串。如果它们要成为最后需要单独步骤的符号。

    【讨论】:

      【解决方案2】:

      我找到了解决这个问题的方法:) 我没有初始化我的有效负载以使用数组写入 CSV 文件,而是使用带有分隔符的字符串对其进行初始化:

      payload = {name: 'Dimitrius', errors: ['error1', 'error2', 'error3'].join('|') }
      CSV.open(file_name, "a+") do |csv|
        csv.add_row(payload.values)
      end
      

      现在,我的 CSV 文件将生成以下结果:

      csv_text = File.read(file_name)
      csv = CSV.parse(csv_text, headers: false)
      puts csv # [["Dimitrius", "error1|error2|error3"]
      

      而且,我可以轻松地将这个字符串 "error1|error2|error3" 转换为执行 .split 的数组,如下所示:

      errors = "error1|error2|error3".split('|')
      errors # ['error1', 'error2', 'error3']
      

      我不确定这是否是在 CSV 中处理数组的最佳解决方案,但对我来说是可行的,因为错误字符串永远不会包含运算符 |

      如果有人有其他想法,我很乐意讨论:)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多