我有一个Hash
看起来像这样的数据结构():
{
foo: "Test string",
bar: [475934759, 5619827847]
}
我正在尝试从中创建一个校验和,Hash
以检查将来是否相等。我尝试使用 的hash
方法Hash
,这产生了令人满意的漂亮哈希,但事实证明,在Hash
重新启动解释器后,同样会产生不同的哈希。
我真的只是希望能够从Hash
,String
或Array
实例创建一个 ~128 位校验和。
这可能吗?
您可以根据对象的Marshal 转储或JSON表示来计算您自己的哈希值。
这将计算 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}
为了建立上面@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
如果您需要生成哈希内容的校验和,无论数据的顺序如何,使用 Marshal 或 sort 或其他技术都行不通。
到目前为止,我发现的唯一可靠方法如下:
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"