如何将一个对象保存到文件中?

25

我想将一个对象保存到文件中,然后从文件中轻松地读取它。作为一个简单的例子,假设我有以下的三维数组:

m = [[[0, 0, 0],
 [0, 0, 0],
 [0, 0, 0]],
[[0, 0, 0],
 [0, 0, 0],
 [0, 0, 0]]]

有没有一个简单的Ruby API可以让我在不编写解析器来解释文件数据的情况下实现这一点?在我提供的示例中很容易,但随着对象变得更加复杂,使对象持久化变得烦人。

3个回答

53
你需要在将对象保存到文件之前对它们进行序列化,以便稍后可以反序列化并检索它们。正如Cory所提到的,有两个广泛使用的标准序列化库:MarshalYAMLMarshalYAML都使用dumpload方法进行序列化和反序列化。
以下是如何使用它们:
m = [
     [
      [0, 0, 0],
      [0, 0, 0],
      [0, 0, 0]
     ],
     [
      [0, 0, 0],
      [0, 0, 0],
      [0, 0, 0]
     ]
    ]

# Quick way of opening the file, writing it and closing it
File.open('/path/to/yaml.dump', 'w') { |f| f.write(YAML.dump(m)) }
File.open('/path/to/marshal.dump', 'wb') { |f| f.write(Marshal.dump(m)) }

# Now to read from file and de-serialize it:
YAML.load(File.read('/path/to/yaml.dump'))
Marshal.load(File.read('/path/to/marshal.dump'))

你需要注意与文件读写相关的文件大小和其他细节问题。

当然,更多信息可以在API文档中找到。


没有足够的权限编辑您的答案并将其添加到其中。 :-) - Swanand
1
@Swanand,对我来说,在Ruby 2.0.0中这个方法不起作用,应该改为File.open('/path/to/file.extension', 'wb') {|f| f.write(Marsshal.dump(m)) }File.open('/path/to/file.extension', 'rb') {|f| m = Marshal::load(f)}(将其设置为二进制读写,这可能是标准操作,否则会出现ASCI-8BIT到UTF8转换错误)- 如果m是手动定义类的对象,则必须确保在Marshall :: load之前加载此类(否则会出现类/模块未知错误)。 - Yo Ludke
@YoLudke - 我认为在Marshal#load之前加载类/模块是理所当然的,但你提到它是正确的。此外,这段代码是在1.8.7中编写的(也适用于1.9.3),但感谢您指出2.0.0的情况。我将尝试并更新答案。 - Swanand
1
Marshal并不是一个很好的持久化工具,其格式取决于Ruby版本,并且没有办法在新的Rubies中解码旧的Marshal格式。"在正常使用中,编组只能加载使用相同主版本号和等于或低于次要版本号编写的数据。" - mu is too short
@muistooshort 同意。 - Swanand
不要忘记添加require 'yaml' - Nick

15

3
Marshal 不是一个很好的持久化工具,其格式取决于 Ruby 版本,并且在新版本的 Ruby 中没有办法解码旧版本的 Marshal 格式。在正常使用中,Marshal 只能加载由相同主版本号和等于或较低次版本号写入的数据。详情请见:http://ruby-doc.org/core/Marshal.html。 - mu is too short

3

YAML和Marshal是最明显的选择,但根据您计划对数据进行的操作,sqlite3也可能是一个有用的选项。

require 'sqlite3'

m = [[[0, 0, 0],
 [0, 0, 0],
 [0, 0, 0]],
[[0, 0, 0],
 [0, 0, 0],
 [0, 0, 0]]]

db=SQLite3::Database.new("demo.out")
db.execute("create table data (x,y,z,value)")
inserter=db.prepare("insert into data (x,y,z,value) values (?,?,?,?)")
m.each_with_index do |twod,z|
  twod.each_with_index do |row,y|
    row.each_with_index do |val,x|
      inserter.execute(x,y,z,val)
    end
  end
end

我不建议使用 sqlite3,因为它是从数据中推断类型而不是使用严格的类型系统,所以存在许多数据类型不一致的问题:https://www.sqlite.org/datatype3.html - thesmart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接