【问题标题】:Is there a quick and easy way to create a checksum from Ruby's basic data structures?有没有一种快速简便的方法可以从 Ruby 的基本数据结构中创建校验和?
【发布时间】:2013-10-15 11:42:42
【问题描述】:

我有一个看起来像这样的数据结构 (Hash):

{
    foo: "Test string",
    bar: [475934759, 5619827847]
}

我正在尝试从该Hash 创建一个校验和,以检查将来是否相等。我尝试使用Hashhash 方法,得到了令人满意的漂亮哈希,但事实证明,相同的Hash 在重新启动解释器后会产生不同的哈希。

我真的只是希望能够从 HashStringArray 实例创建 ~128 位校验和。

这可能吗?

【问题讨论】:

  • 如果哈希或数组的内部排序很重要,那么这不是一个简单的操作。如果内部订单不重要,有什么理由不能只对 json 或 yaml 内容进行摘要?
  • @mcfinnigan:数组的顺序很重要,但哈希的顺序并不重要。我会谷歌文摘。

标签: ruby hash checksum


【解决方案1】:

您可以根据对象的Marshal dumpJSON 表示来计算自己的哈希值。

这会计算 Marshal 转储的 MD5 哈希:

require 'digest/md5'

hash = {
  foo: "Test string",
  bar: [475934759, 5619827847]
}

Marshal::dump(hash)
#=> "\x04\b{\a:\bfooI\"\x10Test string\x06:\x06ET:\bbar[\ai\x04'0^\x1Cl+\b\x87\xC4\xF7N\x01\x00"

Digest::MD5.hexdigest(Marshal::dump(hash))
#=> "1b6308abdd8f5f6290e2825a078a1a02"

更新

您可以实施自己的策略,但我不建议更改核心功能:

class Hash
  def _dump(depth)
    # this doesn't cause a recursion because sort returns an array
    Marshal::dump(self.sort, depth)
  end

  def self._load(marshaled_hash)
    Hash[Marshal::load(marshaled_hash)]
  end
end

Marshal::dump({foo:1, bar:2})
#=> "\x04\bu:\tHash\e\x04\b[\a[\a:\bbari\a[\a:\bfooi\x06"

Marshal::dump({bar:2, foo:1})
#=> "\x04\bu:\tHash\e\x04\b[\a[\a:\bbari\a[\a:\bfooi\x06"

Marshal::load(Marshal::dump({foo:1, bar:2}))
#=> {:bar=>2, :foo=>1}

【讨论】:

  • 这叫genious,谁知道Ruby的core和stnd lib进出...谢谢Stefan...:)
  • 这几乎正是我正在寻找的。我看到的唯一问题是{foo: "bar", meow: 123}{meow: 123, foo: "bar"} 会产生不同的哈希值,即使我认为它们是相同的。对此有什么想法吗?
  • 一种选择是从哈希中提取键/值对,按键的字母顺序排列到一个数组中,然后对该数组进行 hexdigest。
  • @Codemonkey 为什么你的哈希值以不同的方式排序?
  • @Stefan:关键是哈希中的顺序并不重要,因此两个哈希中不同的内部顺序不应该意味着它们产生不同的校验和。
【解决方案2】:

要建立在上面@Stefan 的答案上,如果哈希的顺序很重要,请在将输出推送到 Mashall 之前对其进行排序。

require 'digest/md5'

hash = {
  'foo'=> "Test string",
  'bar'=> [475934759, 5619827847]
}

puts Digest::MD5.hexdigest(Marshal::dump(hash.collect{|k,v| [k,v]}.sort{|a,b| a[0] <=> b[0]})) 
# 8509c564c0ae8dcb6c2b9b564ba6a03f

hash = {
  'bar'=> [475934759, 5619827847],
  'foo'=> "Test string"
}

puts Digest::MD5.hexdigest(Marshal::dump(hash.collect{|k,v| [k,v]}.sort{|a,b| a[0] <=> b[0]})) 
# 8509c564c0ae8dcb6c2b9b564ba6a03f 

【讨论】:

  • 不是hash.collect{|k,v| [k,v]}.sort{|a,b| a[0] &lt;=&gt; b[0]},你就不能用hash.sort吗?
  • @Codemonkey 很有趣,它似乎做同样的事情。试试看:-)
  • 尽管如此,这并不是最终的解决方案,因为散列可能包含其他顺序不同的散列。我不认为DigestJSON 是答案。至少不会,除非我做一个递归函数。
  • 一般情况下排序不是解决方案:{:a =&gt; 1, "b" =&gt; 2}.sort #=&gt; ArgumentError
  • collect/sort 对哈希进行排序的解决方案通常都无效:('a' &lt;=&gt; :b) == nil
【解决方案3】:

如果您需要为哈希的内容生成校验和,无论数据的顺序如何,使用 Marshal 或排序或其他技术都行不通。

到目前为止,我发现的唯一可靠方法如下:

require 'digest/md5'

hash1 = { "a" => 1, "b" => "2", c: { d: "3" } }
hash2 = { c: { d: "3" }, "a" => 1, "b" => "2" }

Digest::MD5.hexdigest(Marshal.dump(hash1)) # => "5def3b2cbdddd3aa6730b6d0527c2d79"
Digest::MD5.hexdigest(Marshal.dump(hash2)) # => "8155698ccfb05b8db01490e9b9634fd9"

Digest::MD5.hexdigest(hash1.to_s.chars.sort.join) # => "812bb65d65380fc1e620a9596806cc35"
Digest::MD5.hexdigest(hash2.to_s.chars.sort.join) # => "812bb65d65380fc1e620a9596806cc35"

【讨论】:

  • {"a" =&gt; "b"}{"b" =&gt; "a"} 在这种情况下会测试为相同
  • 这就是重点,因此我的评论whatever the order of the data :)
  • 哦,我的错,我误读了您的评论。你是对的,这确实是一个重要的缺点:/所以我想没有灵丹妙药:(我猜只有深度排序才能做到这一点:(
猜你喜欢
  • 1970-01-01
  • 2014-01-02
  • 1970-01-01
  • 1970-01-01
  • 2010-09-06
  • 2016-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多