保存与加载数据

将该程序与前一章中的 yaml_dump2.rb 进行比较:

marshal1.rb
  1. f = File.open( 'friends.sav', 'w' )
  2. Marshal.dump( ["fred", "bert", "mary"], f )
  3. f.close
  4. File.open( 'morefriends.sav', 'w' ){ |friendsfile|
  5. Marshal.dump( ["sally", "agnes", "john" ], friendsfile )
  6. }
  7. File.open( 'morefriends.sav' ){ |f|
  8. $arr= Marshal.load(f)
  9. }
  10. myfriends = Marshal.load(File.open( 'friends.sav' ))
  11. morefriends = Marshal.load(File.open( 'morefriends.sav' ))
  12. p( myfriends )
  13. p( morefriends )
  14. p( $arr )

除了每次出现的 YAML(如 YAML.dumpYAML.load)都已被 Marshal 替换之外,这两个程序几乎完全相同。此外,Marshal 作为标准“内置”(built in)于 Ruby 中,因此你无需“引入”(require)任何额外的文件即可使用它。

但是,如果你查看生成的数据文件(例如 ‘friends.sav’),你会立即看到存在的重要差异。YAML 文件采用纯文本格式,而 Marshal 文件采用二进制格式。因此,虽然你可以阅读某些字符,例如字符串中的字符,但你不能简单地在文本编辑器中加载已保存的数据并对其进行修改。

与 YAML 一样,大多数数据结构都可以使用 Marshal 自动序列化,只需转储顶级(top-level)对象并在想要重建其下的所有对象时加载它。举个例子,看看我的小冒险游戏程序。在上一章中,我解释了如何通过转储和加载 Map 对象 mymap(参见 gamesave_y.rb)来保存和恢复包含了包含 Treasures 的 Rooms 的 Map 对象。使用 Marshal 替代 YAML 可以做同样的事情:

gamesave_m.rb
  1. File.open( 'game.sav', 'w' ){ |f|
  2. Marshal.dump( mymap, f )
  3. }
  4. File.open( 'game.sav' ){ |f|
  5. mymap = Marshal.load(f)
  6. }

在一些特殊情况下,对象不能如此容易地被序列化。Ruby 的 Marshal 模块(marshal.c)中的代码记录了这些异常:如果要转储的对象包括绑定(bindings),例程(procedure)或方法(method)对象,IO 类的实例或单例对象(singleton objects),则会抛出 TypeError。稍后在考虑如何通过编排(marshaling)来保存单例(singletons)对象时,我会看一个与之相关的示例。