【问题标题】:Ruby strptime not working while reading fileRuby strptime在读取文件时不起作用
【发布时间】:2014-07-26 16:51:56
【问题描述】:

我有以下代码:

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>'

文件内容:

2014 年 1 月 30 日凌晨 1:00 1251.6 2014 年 1 月 30 日凌晨 2:00 1248 2014 年 1 月 30 日凌晨 3:00 1246.32 2014 年 1 月 30 日凌晨 4:00 1242.96 2014 年 1 月 30 日上午 5:00 1282.08 2014 年 1 月 30 日上午 6:00 1293.84 2014 年 1 月 30 日上午 7:00 1307.04 2014 年 1 月 30 日上午 8:00 1337.76 2014 年 1 月 30 日上午 9:00 1357.92

如果我在 IRB 中输入它,它会完美运行:

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

谁能告诉我这里发生了什么?

【问题讨论】:

  • 仔细检查您的split
  • 拆分工作正常,我通过删除 DateTime 行来测试代码。
  • 我们在您的输入文件中找不到任何选项卡,必须假定它们位于“AM”|“PM”指示符之后。
  • 是的,他们是……我没有把它放在样本上,只是想显示内容,但你是对的,标签在 AM|PM 之后
  • 您需要为问题提供工作数据。不要故意以使您的代码示例失败的方式更改输入。我们期望有一个输入和代码的工作示例来演示/复制您所看到的问题。照原样,即使我用制表符替换有问题的空格,我也无法复制问题。

标签: ruby file byte-order-mark endianness


【解决方案1】:

您的示例数据与您的代码尝试处理的数据不匹配,因此我对此进行了调整。另外,它需要一些东西来表明 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(“字节顺序标记”)。前两个字节表示文件中字节顺序的"endianness"。此外,每个字符实际上占用两个字节。这是一个 UTF-16LE 文件,因为 fffe 缺少一个位 (0xfe == 0b11111110),表示字节对的结尾小于第一个字节。如果是feff,那就是“大端”:

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 --- 1251.6 2 2014 年 1 月 30 日凌晨 2:00 --- 1248 3 2014 年 1 月 30 日凌晨 3:00 --- 1246.32 4 2014 年 1 月 30 日凌晨 4:00 --- 1242.96 5 2014 年 1 月 30 日上午 5:00 --- 1282.08 6 2014 年 1 月 30 日上午 6:00 --- 1293.84 7 1/30/2014 上午 7:00 --- 1307.04 8 1/30/2014 上午 8:00 --- 1337.76 9 2014 年 1 月 30 日上午 9:00 --- 1357.92

【讨论】:

  • 我试过这段代码,它可以工作,但我需要它来读取文件。我把我正在使用的示例文件的链接放在这里了:dropbox.com/s/0on4knx6ikt8pxr/test.csv
  • 并非如此。几年前我刚碰到这些特殊的墙壁。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-10
  • 2014-11-17
  • 1970-01-01
相关资源
最近更新 更多