【问题标题】:How do you use Ruby CSV converters?你如何使用 Ruby CSV 转换器?
【发布时间】:2012-02-12 18:56:58
【问题描述】:

假设你有以下文件:

textfield,datetimefield,numfield
foo,2008-07-01 17:50:55.004688,1
bar,2008-07-02 17:50:55.004688,2

读取 .csv 的 Ruby 代码类似于:

#!/usr/bin/env ruby

require 'csv'

csv = CSV($stdin, :headers => true, :converters => :all)
csv.each do |row|
  print "#{row}"
  the_date = row['datetimefield'].to_date
end

该代码给出了以下错误消息:

./foo2.rb:8:in `block in <main>': undefined method `to_date' for "2008-07-01 17:50:55.004688":String (NoMethodError)

什么给了?

我读过the docs,但我不明白。

编辑:是的,我可以单独解析这些字段。这个问题的重点是我想学习如何使用文档转换器功能。

【问题讨论】:

    标签: ruby csv


    【解决方案1】:

    有效!!!代码的可执行版本:

    #Define test data
    src =
      "textfield,datetimefield,numfield\n" +
      "foo,2008-07-01 17:50:55.004688,1\n" +
      "bar,2008-07-02 17:50:55.004688,2"
    
    require 'csv'
    require 'time'
    
    CSV::Converters[:mytime] = lambda{|s|
      begin
        Time.parse(s)
      rescue ArgumentError
        s
      end
    }
    
    csv = CSV(src, :headers => true, :converters => [:mytime])
    array_of_hash_objects = csv.to_a.map {|row| row.to_hash }
    
    array_of_hash_objects.each do |row|
      print "#{row}"
      the_date = row['datetimefield'].to_date
      puts
      print the_date
      puts
    end
    
    CSV::Converters[:time2date] = lambda{|s|
      begin
        Time.parse(s).to_date
      rescue ArgumentError
        s
      end
    }
    
    csv = CSV(src, :headers => true, :converters => CSV::Converters.keys + [:time2date])
    array_of_hash_objects = csv.to_a.map {|row| row.to_hash }
    
    array_of_hash_objects.each do |row|
      print "#{row}"
      puts
      p row['datetimefield'] #Date-field
    end
    

    =============

    输出:

    {"textfield"=>"foo", "datetimefield"=>2008-07-01 17:50:55 -0400, "numfield"=>"1"}
    2008-07-01
    {"textfield"=>"bar", "datetimefield"=>2008-07-02 17:50:55 -0400, "numfield"=>"2"}
    2008-07-02
    {"textfield"=>"foo", "datetimefield"=>2008-07-01 17:50:55 -0400, "numfield"=>1}
    2008-07-01 17:50:55 -0400
    {"textfield"=>"bar", "datetimefield"=>2008-07-02 17:50:55 -0400, "numfield"=>2}
    2008-07-02 17:50:55 -0400
    

    【讨论】:

      【解决方案2】:

      您在问如何使用转换器。

      我的示例定义了新转换器mytime 的用法。 如果可能,转换器 my_time 会尝试构建时间对象。

      #Define test data
      src = <<csv
      textfield,datetimefield,numfield
      foo,2008-07-01 17:50:55.004688,1
      bar,2008-07-02 17:50:55.004688,2
      csv
      
      require 'csv'
      require 'time'
      CSV::Converters[:mytime] = lambda{|s| 
        begin 
          Time.parse(s)
        rescue ArgumentError
          s
        end
      }
      
      csv = CSV(src, :headers => true, :converters => [:mytime])
      csv.each do |row|
        print "#{row}"
        the_date = row['datetimefield'].to_date
      end
      

      使用这种技术,您还可以定义一个转换器来从类似时间的字符串中创建一个日期。此解决方案还保留所有其他转换器。

      #define test data
      src = <<csv
      textfield,datetimefield,numfield
      foo,2008-07-01 17:50:55.004688,1
      bar,2008-07-02 17:50:55.004688,2
      csv
      
      require 'csv'
      require 'time'
      CSV::Converters[:time2date] = lambda{|s| 
        begin 
          Time.parse(s).to_date
        rescue ArgumentError
          s
        end
      }
      
      csv = CSV(src, :headers => true, :converters => CSV::Converters.keys + [:time2date])
      csv.each do |row|
        print "#{row}"
        p row['datetimefield'] #Date-field
      end
      

      【讨论】:

        【解决方案3】:

        您的日期时间与 CSV 用来决定是否尝试进行日期时间转换的 CSV::DateTimeMatcher 正则表达式不匹配。从外观上看,它这样做是因为你有小数秒。

        您可以覆盖该常量或编写自己的转换器(DateTime.parse 似乎对您的字符串很满意)

        【讨论】:

        • 啊哈,干得好。如果我删除小数秒,它会起作用。因此,我接受你的回答。这令人惊讶,因为文档说“:date_time: Converts any field DateTime::parse() 接受”。和“:all: 所有内置转换器。:date_time 和:numeric 的组合。”。也就是说,:all 使用 :date_time,它使用 DateTime.parse(),你说它接受小数秒。那么,文档是错误的吗? (也就是说,他们声称任何 DateTime.parse() 但实际上有一个不匹配的正则表达式?)
        • @dfrankow 你的推理没有错。如果字符串看起来像日期时间,则 csv.rb 测试中有一个奇怪的正则表达式。它不承认有效日期。我提交了bug
        • 另一种说法: :all 以上的使用本质上是文档的意图? (我在 Google 上找不到任何真实的例子。)
        • 我来到这里是因为 "03/02/2012" 这样的格式没有被识别(首先是 mm/dd/yyyy 而不是 yyyy),而且我不知道如何在文档中覆盖 DateMatcher
        • 要关闭@dfrankow 提交的错误的循环,OP 的原始测试失败的原因和错误被拒绝的原因是“03/02/2012”不明确(MM/DD/YYY ? DD/MM/YYYY?),因此 ruby​​ 没有为该日期时间定义默认格式。
        猜你喜欢
        • 2014-03-21
        • 2016-04-28
        • 2012-05-01
        • 2019-05-12
        • 1970-01-01
        • 1970-01-01
        • 2012-12-21
        • 2013-10-16
        • 1970-01-01
        相关资源
        最近更新 更多