【问题标题】:Why does "23 Dogs" get parsed to 23 november 2015 in pry, but "3 Dogs" gives a parser error?为什么“23 Dogs”会被解析到 2015 年 11 月 23 日,但“3 Dogs”会出现解析器错误?
【发布时间】:2015-11-23 20:15:38
【问题描述】:

我在 Twitter 上找到了以下代码 sn-p(查看帖子历史记录的来源)。

[5] pry(main)> Date.parse('3 Dogs')
ArgumentError: invalid date
[6] pry(main)> Date.parse('23 Dogs')
=> Mon, 23 Nov 2015

这只是一个正在撬动的复活节彩蛋吗?如果是这样,为什么这个特定的日期和结果?如果不是彩蛋,为什么23 Dogs 解析为日期,而3 Dogs 不解析?

【问题讨论】:

  • 这只是一个正在撬动的彩蛋吗?是的,只有撬动才会产生这种结果。如果您在 ruby​​ 程序或 irb 中运行该代码,则 Date.parse() 将为该数据生成:“Hello world”。 Date.parse('23')Date.parse('3') 得到什么?
  • 其实在这两种情况下你都应该得到NameError: uninitialized constant Date
  • @7stud 我不知道,我没有安装 pry。如果仅 pry 给出该结果,是因为它是这样硬编码的,还是因为 pry 具有不同的 Date 解析器?为什么会给出这个结果?
  • @CarySwoveland 我编辑以说明第 1、2、4 和 5 点。“复活节彩蛋”是 IT 行业和互联网中的一个众所周知的术语,表示已添加为笑话或文化参考。如果不使以下部分答案无效,我无法从问题中删除它。
  • 这好多了,imo,但重要的是它曾经是并且现在是一个非常好的问题。我希望你得到一个明确的答案,因为我也想知道发生了什么。

标签: ruby date parsing pry


【解决方案1】:

这与 Pry 无关。如果您检查Date::parse 的文档,您会看到,“如果可选的第二个参数 [comp] 为真 [默认] 并且检测到的年份在“00”到“99”的范围内,则认为年份一个 2 位数的表格,并使其填满。”。

这确实很奇怪。它检测到"23""00".."30" 范围内(但不是"3""0".."9" 中的任何一个),因此它断定这是一个日期。请注意,"31".."99" 范围内的任何值也会引发异常。我预计"30" 是该范围的高端,因为目前是 11 月,11 月有 30 天。然后它似乎丢弃了该信息并使用当前的年份和月份,并假设日期是"23"(或者如果输入了"31",则引发异常)。谁能解释一下发生了什么的细节?

【讨论】:

  • 我知道他们将 23 解析为 23,但为什么“Dogs”会被解析为“2015 年 11 月”?为什么 23 被解析为有效日期,而 3 没有?
  • 关于Date.parse() 要记住的一点是,它会丢弃无法解析的数据并解析剩下的数据。 “狗”对结果没有影响。如果您尝试Date.parse('23')Date.parse('23 cats')Date.parse('23 Obamas'),您会得到相同的结果。
  • 再举一个例子:irb(main):006:0> Date.parse('On 23 March I got 3 new Dogs') ==> #<Date: 2015-03-23 ((2457105j,0s,0n),+0s,2299161j)> 该例子中唯一解析的是“23 March”
  • @KenB,是的,但我们仍然需要了解(为了能够在晚上睡觉)为什么 Ruby 似乎在说 "23" 是一年! [短暂停顿]"23" 是这一天!”。
  • @CarySwoveland 我不明白......在我看到的所有示例中,无论是在这个 Q/A 还是在我的终端中,'23' is 被解释作为一天。最初的例子是Date.parse('23 Dogs') => Mon, 23 Nov 2015。或者您的意思是 Ruby 文档说它应该将它作为年份来阅读?您的测试似乎表明情况并非如此,因为例如它突破了 30。
【解决方案2】:

所以它与撬无关。我可以用完全不加载 pry 的 ruby​​ 代码在 ruby​​ 2.2.2 中重现您的报告。

那么为什么 Date.parse 愿意解析“23 条狗”并想出一些东西呢?我不知道。我会说这是 Date 解析中的一些特质,甚至是错误;它试图解析各种事物,但这会导致一些奇怪的边缘情况。

要对已知固定格式的日期进行更可预测的解析,请改用Date#strptime。要对不可预测格式的自然语言日期进行更复杂的解析,请使用 chronic gem。

就个人而言,我从不直接使用Date.parse,因为它有点不可预测,而是使用这两种方法之一。 (或特定的格式解析方法,如Date.iso8601)。

我试图查看Date.parse 的 MRI 代码,因为我很好奇我是否能弄清楚它在做什么。但很快就迷失在我无法理解或遵循的 C 代码中,不得不放弃。

有趣的是,这也可以在 JRuby 1.7.10 中重现(我还没有安装 jruby 9x)。 “23只狗”解析为同样的东西,“3只狗”提出。嗯,也许 JRuby Java 代码比 MRI 的 C 代码更容易理解。但是我没有时间尝试通过/调试 JRuby 中的 Date#parse 正在做的事情。它的核心也许开始于here,尽管我可能没有找到当前版本实现的正确位置。您可以看到它尝试按顺序根据多种不同格式解析日期,当它根据某种格式成功解析时停止。我们可以猜测该列表中有一些奇怪的格式以某种方式成功解析“23 只狗”而不是“3 条狗”。这很可能不是复活节彩蛋,也不是故意的;这只是尝试通过尝试猜测日期的格式并按顺序尝试各种格式来解析日期的奇怪副作用,而不是非常复杂的算法。

更新好的,至少在我正在查看的 jruby 代码中(可能不是当前的实现,而是一些实现)

  • 最终,在尝试其他可能失败的解析后,它会尝试Date._parse_ddd -- 两个输入。

  • Date._parse_ddd("23 dogs", e) 返回 true,并用 mday 组件填充 Date::Parse::Bag,但 Date._parse_ddd("3 dogs", e) 返回 false 并且不填充 Bag。所以其他一切都从这里开始。

  • 如果我们看一下Date._parse_ddd 的实现……就会发现一些怪异的正则表达式和奇怪的逻辑。可能从 MRI 复制以与 MRI 一致,或以其他方式与 MRI 行为一致。

  • 我不想进一步调试。如果你愿意,你可以。如您所见,JRuby 实现实际上是用 ruby​​ 编写的,甚至不是用 Java 编写的。

您或我或其他人可以尝试进一步调试(甚至可能在 JRuby 标准库实现上使用交互式调试器)以找出 确切发生了什么。但我相信答案基本上是“这是 Date.parse 的一个奇怪的副作用,并不真正知道它的输入格式,但只是尝试了一堆东西,使用不是很复杂的算法,有时会发生奇怪的事情”

更多更新:请注意,Date.parse("03 dogs") 会解析而不是提升。所以它决定的两个数字是可解析的,一个则不能。但当然Date.parse("3 May") 工作正常。并不是Date.parse 需要两位数的日期,只是它尝试了一大堆不同的解析方式,并且会正确捕捉到实际的好日期,但可能会通过一种认为的方式捕捉到不好的日期这似乎足够好,但在这种情况下是错误的。

更多想法所以它不是故意这样解析的。这是旨在捕捉其他日期的启发式规则的副产品。由于代码没有注释,我们不能准确地说出哪些部分要捕获的日期类型。这是一堆拼凑在一起的东西,试图以各种格式(包括国际格式)捕捉日期。

您可以查看测试以了解它要捕获的各种日期。或者您可以尝试通过代码来准确了解哪些行会导致您看到的行为。代码令人困惑——对我们大多数人来说,尤其是 MRI 中的 C 代码。 JRuby 中的纯 ruby​​ 代码对于我们 ruby​​ 爱好者来说当然更具可读性。由于浏览代码既混乱又耗时,而且几乎没有什么好处(谁在乎?),你可能不会让其他人为你做这件事。

【讨论】:

  • 我也查看了 C 源代码,但没有进一步了解。我们需要熟悉它的人来告诉我们发生了什么。
  • 我注意到的是,由于 23Dogs 之间的空格,第二个捕获组实际上是 3 个字符长,而不是 2 个。我不确定这会如何改变事情.您可以尝试执行 Date.parse("23dogs") 并查看您得到的响应吗?
  • 是的,在 MRI 中,“23dogs”返回与“23 Dogs”相同的日期对象。
猜你喜欢
  • 2018-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-16
相关资源
最近更新 更多