为什么在Ruby中使用Marshal转储和加载Hash会抛出FormatError?

Sat*_*hat 11 ruby marshalling

我正在运行从RubyInstaller安装的Ruby .这是版本:

C:\Users\Sathya>ruby -v
ruby 1.9.2p290 (2011-07-09) [i386-mingw32]
Run Code Online (Sandbox Code Playgroud)

这是确切的代码,它抛出错误:

hashtime = Hash.new(Time.mktime('1970'))
hashtime[1]  = Time.now
=> 2011-10-04 19:26:53 +0530
print hashtime
{1=>2011-10-04 19:26:53 +0530}=> nil
hashtime[1]  = Time.now
=> 2011-10-04 19:27:20 +0530
print hashtime
{1=>2011-10-04 19:27:20 +0530}=> nil
File.open('timehash','w') do |f|
  f.write Marshal.dump(hashtime)
end
=> 56  
Run Code Online (Sandbox Code Playgroud)

现在,尝试加载它.

Marshal.load (File.read('timehash'))
Run Code Online (Sandbox Code Playgroud)

给出错误:

ArgumentError: dump format error for symbol(0x42)
        from (irb):10:in `load'
        from (irb):10
        from C:/Ruby192/bin/irb:12:in `<main>'
Run Code Online (Sandbox Code Playgroud)

为什么这会引发错误?我做错了什么,或者这是一个错误?

我正在运行Windows 7 Ultimate,64位


以下是您提到的已编辑调试代码的结果:

hashtime = Hash.new
=> {}
hashtime[1] = Time.now
=> 2011-10-04 20:49:52 +0530
hashdump = Marshal.dump(hashtime)
=> "\x04\b{\x06i\x06Iu:\tTime\r\x8F\xE4\e\x80<\xADGO\x06:\voffseti\x02XM"
hashtime = Marshal.load (hashdump)
=> {1=>2011-10-04 20:49:52 +0530}
print hashtime
{1=>2011-10-04 20:49:52 +0530}=> nil  
Run Code Online (Sandbox Code Playgroud)

编辑结果2:

hashtime = Hash.new
=> {}
hashtime[1] = Time.now
=> 2011-10-04 21:04:24 +0530
hashdump = Marshal.dump(hashtime)
=> "\x04\b{\x06i\x06Iu:\tTime\r\x8F\xE4\e\x80\x92o\x8C\x89\x06:\voffseti\x02XM"
print "hashdump: #{hashdump}"
ÅS?ÇÆoîë?:?offseti?XM=> nile
File.open('timehash','w') do |f|
 f.write hashdump
end
=> 36
hashdump2 = File.read('timehash')
=> "\x04\b{\x06i\x06Iu:\tTime\n\x8F\xE4\e\x80\x92o\x8C\x89\x06:\voffseti\x02XM"
print "hashdump2: #{hashdump2}"
hashdump2:{?i?Iu:       Time
ÅS?ÇÆoîë?:?offseti?XM=> nil
hashtime2 = Marshal.load (hashdump2)
ArgumentError: dump format error for symbol(0x8c)
        from (irb):73:in `load'
        from (irb):73
        from C:/Ruby192/bin/irb:12:in `<main>'  
Run Code Online (Sandbox Code Playgroud)

一些角色没有出来,这是一个截图:

哈希转储名帅


现在我得到一个时间格式不同的错误

hashtime = Hash.new
=> {}
hashtime[1] = Time.now
=> 2011-10-04 21:23:15 +0530
hashdump = Marshal.dump(hashtime)
=> "\x04\b{\x06i\x06Iu:\tTime\r\x8F\xE4\e\x80\xB9\xE1\xFB\xD4\x06:\voffseti\x02X
M"
print "hashdump: #{hashdump}"
Å??Ç?ß???:?offseti?XM=> nile
File.open('timehash','wb') do |f|
 f.write hashdump
end
=> 36
hashdump2 = File.read('timehash')
=> "\x04\b{\x06i\x06Iu:\tTime\n\x8F\xE4\e\x80\xB9\xE1\xFB\xD4\x06:\voffseti\x02X
M"
print "hashdump2: #{hashdump2}"
hashdump2:{?i?Iu:       Time
Å??Ç?ß???:?offseti?XM=> nil
hashtime2 = Marshal.load (hashdump2)
TypeError: marshaled time format differ
        from (irb):10:in `_load'
        from (irb):10:in `load'
        from (irb):10
        from C:/Ruby192/bin/irb:12:in `<main>'
Run Code Online (Sandbox Code Playgroud)

Jos*_*osh 12

您需要通过附加b到文件模式以二进制模式写入文件:

File.open('timehash','wb') do |f|
  f.write Marshal.dump(hashtime)
end
Run Code Online (Sandbox Code Playgroud)

您可以通过比较写入磁盘之前的字符串(来自我们的调试)与读回之后的比较来看出这是问题:

=> "\x04\b{\x06i\x06Iu:\tTime\r\x8F\xE4\e\x80\x92o\x8C\x89\x06:\voffseti\x02XM"
=> "\x04\b{\x06i\x06Iu:\tTime\n\x8F\xE4\e\x80\x92o\x8C\x89\x06:\voffseti\x02XM"
                             ^^
Run Code Online (Sandbox Code Playgroud)

a \r(回车)正在改为\n(换行)

但是,好像即使二进制修改你的系统是不服从你和正在发生变化\r,以\n...所以让我们尝试对数据进行编码为base64:

File.open('timehash','w') do |f|
  hashtime_marshal = Marshal.dump(hashtime)
  f.write [hashtime_marshal].pack("m")
end

hashtime_encoded = File.read('timehash')
hashtime = Marshal.load( hashtime_encoded.unpack("m")[0] )
Run Code Online (Sandbox Code Playgroud)

如果有效,请告诉我?


旧信息:

不要传递任何东西Hash.new:

>> hashtime = Hash.new
=> {}
>> hashtime[1] = Time.now
=> Tue Oct 04 10:57:49 -0400 2011
>> hashtime
=> {1=>Tue Oct 04 10:57:49 -0400 2011}
>> File.open('timehash','w') do |f|
?>   f.write Marshal.dump(hashtime)
>> end
=> 22
>> Marshal.load (File.read('timehash'))
(irb):10: warning: don't put space before argument parentheses
=> {1=>Tue Oct 04 10:57:49 -0400 2011}
Run Code Online (Sandbox Code Playgroud)

文档说明obj参数为Hash.new默认值...它应该像你一样工作...我不知道它为什么不...但在你的情况下nil是一个可接受的默认值,只需检查看看如果值是nil,如果是,请使用a Time.mktime('1970')代替它们.

编辑:这解决了我的问题,但是,我在OS X而不是Windows.所以,让我们尝试一些调试.运行以下代码时会发生什么?

hashtime = Hash.new
hashtime[1] = Time.now
hashdump = Marshal.dump(hashtime)
hashtime = Marshal.load (hashdump)
print hashtime
Run Code Online (Sandbox Code Playgroud)

编辑#2:好的.所以Marshal.dump,Marshal.load似乎工作.看起来像文件I/O的东西...请发布以下代码的结果...

hashtime = Hash.new
hashtime[1] = Time.now
hashdump = Marshal.dump(hashtime)
print "hashdump: #{hashdump}"
File.open('timehash','w') do |f|
  f.write hashdump
end
hashdump2 = File.read('timehash')
print "hashdump2: #{hashdump2}"
hashtime2 = Marshal.load (hashdump2)
print hashtime2
Run Code Online (Sandbox Code Playgroud)


der*_*erp 7

而不是File.read尝试File.binread或尝试阅读File.open('timehash', 'rb')


mli*_*elt 5

来自@Josh和@derp的2个答案的组合对我有用.这是代码(写入文件):

hashtime = Hash.new(Time.mktime('1970'))
hashtime[1]  = Time.now
File.open('timehash','wb') do |f|
  f.write Marshal.dump(hashtime)
end
newhash = Marshal.load (File.binread('timehash'))
p newhash
p newhash.default
Run Code Online (Sandbox Code Playgroud)

结果如下:

c:\apps\ruby>ruby h.rb
{1=>2011-10-05 08:09:43 +0200}
1970-01-01 00:00:00 +0100
Run Code Online (Sandbox Code Playgroud)