Ruby strptime在读取文件时无法正常工作

时间:2014-06-05 15:41:51

标签: ruby file byte-order-mark endianness

我有以下代码:

require 'date'

f = File.open(filepath)

f.each_with_index do |line, i|
    a, b = line.split("\t")
    d = DateTime.strptime(a, '%m/%d/%Y %I:%M %p')
    puts "#{a} --- #{b}"
    break unless i < 100
end

我收到以下错误:

c_reader.rb:10:in `strptime': invalid date (ArgumentError)
  from c_reader.rb:10:in `block in <main>'
  from c_reader.rb:6:in `each'
  from c_reader.rb:6:in `each_with_index'
  from c_reader.rb:6:in `<main>'

文件内容:

1/30/2014 1:00 AM   1251.6  
1/30/2014 2:00 AM   1248  
1/30/2014 3:00 AM   1246.32  
1/30/2014 4:00 AM   1242.96  
1/30/2014 5:00 AM   1282.08  
1/30/2014 6:00 AM   1293.84  
1/30/2014 7:00 AM   1307.04  
1/30/2014 8:00 AM   1337.76  
1/30/2014 9:00 AM   1357.92  

如果我将其输入IRB,它可以完美运行:

DateTime.strptime("1/30/2014 2:00 PM", '%m/%d/%Y %I:%M %p')

有人可以告诉我这里发生了什么吗?

1 个答案:

答案 0 :(得分:2)

您的示例数据与您的代码尝试处理的内容不匹配,所以我为此进行了调整。此外,它需要一些东西来表明AM / PM正在受到尊重。

通过对数据的这些调整,您的代码可以正常工作。 strptime返回有效的DateTime对象。

require 'date'

[
  "1/30/2014 1:00 AM\t1251.6",
  "1/30/2014 2:00 AM\t1248",
  "1/30/2014 3:00 PM\t1246.32",
  "1/30/2014 4:00 PM\t1242.96",
].each do |line|
  a, b = line.split("\t")
  puts DateTime.strptime(a, '%m/%d/%Y %I:%M %p')
end
# >> 2014-01-30T01:00:00+00:00
# >> 2014-01-30T02:00:00+00:00
# >> 2014-01-30T15:00:00+00:00
# >> 2014-01-30T16:00:00+00:00

您的数据文件有BOM(&#34;字节顺序标记&#34;)。前两个字节表示文件中字节顺序的"endianness"。另外,每个字符实际占用两个字节。这是一个UTF-16LE文件,因为fffe缺少一个位(0xfe == 0b11111110),表示字节对的结尾小于第一个字节。如果是feff它是&#34; big-endian&#34;:

0000000: fffe 3100 2f00 3300 3000 2f00 3200 3000  ..1./.3.0./.2.0.

Ruby并不知道如何处理这些因为它预期默认的UTF-8。要解决这个问题,你需要告诉Ruby如何解释它。查看IO.new的文档,了解如何定义编码。 Ruby假定数据将是UTF-8,因此传入的数据必须从UTF-16LE转换为UTF-8。这是一种方法:

require 'date'

File.open(
  "test.csv",
  "rb:BOM|UTF-16LE:UTF-8"
) do |fi|
  fi.each_with_index do |line, i|
    a, b = line.split("\t")
    d = DateTime.strptime(a, '%m/%d/%Y %I:%M %p')
    puts "#{ 1 + i } #{a} --- #{b}"
    break unless i < 100
  end
end

运行输出:

1 1/30/2014 1:00 AM --- 1251.6
2 1/30/2014 2:00 AM --- 1248
3 1/30/2014 3:00 AM --- 1246.32
4 1/30/2014 4:00 AM --- 1242.96
5 1/30/2014 5:00 AM --- 1282.08
6 1/30/2014 6:00 AM --- 1293.84
7 1/30/2014 7:00 AM --- 1307.04
8 1/30/2014 8:00 AM --- 1337.76
9 1/30/2014 9:00 AM --- 1357.92