`
simohayha
  • 浏览: 1400985 次
  • 性别: Icon_minigender_1
  • 来自: 火星
社区版块
存档分类
最新评论

ruby way之高级数据存取

    博客分类:
  • ruby
阅读更多
经常我们需要以一种更透明的方式来存储和得到数据.Marshal模块提供了简单的对象持久化,PStore 库建立在这项技术之上。dbm 库使用像hash一样并永久的存贮在磁盘上.

1简单的Marshaling

经常我们需要创建一个对象,然后为了以后的使用保存它.ruby对这种对象持久化(或者说是Marshaling)提供了基本的支持.Marshal 模块能够使程序员序列化和反序列化一个ruby对象.

# array of elements [composer, work, minutes]
    works = [["Leonard Bernstein","Overture to Candide",11],
         ["Aaron Copland","Symphony No. 3",45],
         ["Jean Sibelius","Finlandia",20]]
# We want to keep this for later...
File.open("store","w") do |file|
  Marshal.dump(works,file)
end

# Much later...
File.open("store") do |file|
  works = Marshal.load(file)
end


这里要注意的是这种技术并不是所有的对象能被dump.如果一个对象包含一个低级别的类的对象,他就不能被dump,比如IO、Proc和Binding,单例对象,匿名对象,模块也不能被序列化.

Marshal.dump 还有另外两种参数的形式,如果调用时传入一个参数的话,它将会返回返回一个由字符串表示的数据,其中前两个字节为主版本号和次版本号:

这边的话1.9和1.8的结果是不同的:

s = Marshal.dump(works)
p s[0]  #  4
p s[1]  #  8


上面的结果是1.8打印出的结果,而1.9中,结果将会是\004和\b,也就是说它已经没有版本号了.
经常你如果想载入这些数据,当且仅当主版本号相同,次版本号相等或者小于时才会load。如果ruby解释器使用了verbose 参数的话,则版本号必须完全匹配.

第三个参数limit 只有当被序列化的对象是一个嵌套对象时才有意义,它就是遍历时的深度,当你的对象的嵌套深度大于等于你的limit时,就会抛出ArgumentError:

File.open("store","w") do |file|
  arr = [ ]
Marshal.dump(arr,file,0)      #   in `dump': exceed depth limit
                              #  (ArgumentError)
  Marshal.dump(arr,file,1)

  arr = [1, 2, 3]
Marshal.dump(arr,file,1)      # in `dump': exceed depth limit
                              # (ArgumentError)
  Marshal.dump(arr,file,2)

  arr = [1, [2], 3]
Marshal.dump(arr,file,2)      # in `dump': exceed depth limit
                              # (ArgumentError)
  Marshal.dump(arr,file,3)
end

File.open("store") do |file|
  p Marshal.load(file)          #  [ ]
  p Marshal.load(file)          #   [1, 2, 3]
  p Marshal.load(file)          #   arr = [1, [2], 3]
end


第三个参数的默认值是1,-1的话就是不进行深度检测.

2  更复杂的Marshaling

有时候由于一些限制,我们需要定制我们的序列化。通过创建_load和 _dump 方法我们可以做到定制.这两个方法是当序列化完成时调用的.

接下来的例子中,这个人从出生就赚取5%的利息在他的初始帐户中。 我们这里没有保存年龄和当前的余额,这是因为他们都是时间的函数:

class Person

     attr_reader :name
     attr_reader :age
     attr_reader :balance

     def initialize(name,birthdate,beginning)
       @name = name
       @birthdate = birthdate
       @beginning = beginning
       @age = (Time.now - @birthdate)/(365*86400)
       @balance = @beginning*(1.05**@age)
     end

     def marshal_dump
       Struct.new("Human",:name,:birthdate,:beginning)
       str = Struct::Human.new(@name,@birthdate,@beginning)
       str
     end

     def marshal_load(str)
       self.instance_eval do
         initialize(str.name, str.birthdate, str.beginning)
       end
     end

     # Other methods...

   end

   p1 = Person.new("Rudy",Time.now - (14 * 365 * 86400), 100)
   p [p1.name, p1.age, p1.balance]  # ["Rudy", 14.0, 197.99315994394]

   str = Marshal.dump(p1)
   p2  = Marshal.load(str)

   p [p2.name, p2.age, p2.balance]  # ["Rudy", 14.0, 197.99315994394]


当保存对象时不会计算年龄和当前余额,对象再生的时候,年龄和当前余额会被计算.注意marshal_load 会假设已经存在一个对象,这时就调用initialize就可以了。

3 使用Marshal执行受限制的深度拷贝.

ruby没有深度拷贝的操作,方法dup和clone可能不会是你所想象的那样.一个对象可能包含一个嵌套的对象引用,这时执行一个copy操作,就好像Pick-Up-Sticks的游戏一样.

我们这里提供了一个受限制的深度拷贝,受限制是因为它使用Marshal ,而Marshal是有很多局限的:

def deep_copy(obj)
  Marshal.load(Marshal.dump(obj))
end

a = deep_copy(b)


4 使用PStore进行更好的持久化

PStore 库提供给我们基于文件的ruby对象的持久化存储,一个PStore对象能够持有一个ruby对象的一些层次,每一个层次都有一个通过一个key来标识的root.当一个事务开始时从一个磁盘文件读取层次,当事务结束时将其回写:

require "pstore"

# save
db = PStore.new("employee.dat")
db.transaction do
    db["params"] = {"name" => "Fred", "age" => 32,
                    "salary" => 48000 }
end


# retrieve
require "pstore"
db = PStore.new("employee.dat")
emp = nil
db.transaction { emp = db["params"] }


通常我们能够直接传入PStore 对象给一个事务代码块。

在事务的进行中间我们能够通过commit或者abort方法进行终止.前者保持我们做的改变,后者则是放弃改变:

require "pstore"

# Assume existing file with two objects stored
store = PStore.new("objects")
store.transaction do |s|

  a = s["my_array"]
  h = s["my_hash"]

  # Imaginary code omitted, manipulating
  # a, h, etc.

  # Assume a variable named "condition" having
  # the value 1, 2, or 3...

  case condition
    when 1
      puts "Oops... aborting."
      s.abort   # Changes will be lost.
    when 2
      puts "Committing and jumping out."
      s.commit  # Changes will be saved.
    when 3
      # Do nothing...
  end

  puts "We finished the transaction to the end."
  # Changes will be saved.

end


在事务中,你也可以使用方法roots 来返回一个根的数组,也有一个delete方法来删除一个root:

store.transaction do |s|
  list = s.roots          # ["my_array","my_hash"]
  if s.root?("my_tree")
    puts "Found my_tree."
  else
    puts "Didn't find # my_tree."
  end
  s.delete("my_hash")
  list2 = s.roots         # ["my_array"]
end


5 处理CSV数据

这边我们主要介绍FasterCSV库,因为它比起ruby内置的csv库更快.

CSV模块(csv.rb)能够按照csv的格式生成或者解析数据,对于csv的格式并没有一个统一的观点.FasterCSV库的作者定义了自己的csv的格式的标准:

引用
Record separator: CR + LF

Field separator: comma (,)

Quote data with double quotes if it contains CR, LF, or comma

Quote double quote by prefixing it with another double quote ("-> "")

Empty field with quotes means null string (data,"",data)

Empty field without quotes means NULL (data,,data)


让我们以创建一个文件开始,为了写一个逗号分割的数据,我们可以简单的以写模式打开文件:

require 'csv'

CSV.open("data.csv","w") do |wr|
  wr << ["name", "age", "salary"]
  wr << ["mark", "29", "34500"]
  wr << ["joe", "42", "32000"]
  wr << ["fred", "22", "22000"]
  wr << ["jake", "25", "24000"]
  wr << ["don", "32", "52000"]
end


上面的代码生成一个data.csv文件。

引用
"name","age","salary"
"mark",29,34500
"joe",42,32000
"fred",22,22000
"jake",25,24000
"don",32,52000


下面的程序负责读取:

require 'csv'

CSV.open('data.csv', 'r') do |row|
  p row
end

# Output:
# ["name", "age", "salary"]
# ["mark", "29", "34500"]
# ["joe", "42", "32000"]
# ["fred", "22", "22000"]
# ["jake", "25", "24000"]
# ["don", "32", "52000"]


更高级的特性可以去ruby-doc.org去看.

6 用YAML进行序列化

YAML 的意思是YAML Ain't Markup Language.他就是一种灵活的适合人阅读的一种存储格式。它和xml很类似,可是更优美.

当我们require yaml后,每个对象都将会有一个to_yaml 方法:

require 'yaml'

str = "Hello, world"

num = 237

arr = %w[ Jan Feb Mar Apr ]

hsh = {"This" => "is", "just a"=>"hash."}

puts str.to_yaml

puts num.to_yaml

puts arr.to_yaml

puts hsh.to_yaml

# Output:

# --- "Hello, world"

# --- 237

# ---

# - Jan

# - Feb

# - Mar

# - Apr

# ---

# just a: hash.

# This: is

to_yaml 方法刚好和YAML.load 方法相反,我么能给他一个字符串或者一个流为参数.

假设我们有一个data.yaml 的文件:
引用
---
- "Hello, world"
- 237
-
  - Jan
  - Feb
  - Mar
  - Apr
-
  just a: hash.
  This: is


如果我们现在load这个流,我们将会得到刚才的数组:

require 'yaml'
file = File.new("data.yaml")
array = YAML.load(file)
file.close
p array
# Output:
# ["Hello, world", 237, ["Jan", "Feb", "Mar", "Apr"],
#  {"just a"=>"hash.", "This"=>"is"}]


7 使用Madeleine进行对象的Prevalence

在一些领域,对象的Prevalence很流行,主要的观点是,内存很便宜并且越来越便宜, 而数据库却很小,因此忘掉数据库,保持所有的对象在内存里.

java实现的版本叫做Prevayler,而相应于ruby的版本叫做Madeleine.

Madeleine并不是对每一个人或者每一个程序都适用的.对象的prevalence有它自己的规则和限制。首先所有对象都必须得全部装载到内存里,其次所有的对象都必须能够被序列化(marshalable).

所有的对象都必须是确定的,也就是说有点像数学中的函数,输入相同,输出也一定相同(这意味着使用system clock或者随机数是不可以的).

对象应当尽可能地和所有的IO相隔离,也就是说一般要在prevalence 系统外调用这些IO操作.

最后,每一个改变prevalence 系统的命令都必须像一个命令对象的形式发出.

想研究Madeleine的,只能自己找资料了.

8 使用DBM 库

dbm是一个平台相关的,基于字符串的散列文件存储 机制。它存储一个key和一个和这个key联系在一起的数据,他们都是字符串.

可以看例子:

require 'dbm'

d = DBM.new("data")
d["123"] = "toodle-oo!"
puts d["123"]        # "toodle-oo!"
d.close

puts d["123"]        # RuntimeError: closed DBM file

e = DBM.open("data")
e["123"]                # "toodle-oo!"
w=e.to_hash                # {"123"=>"toodle-oo!"}
e.close

e["123"]                # RuntimeError: closed DBM file
w["123"]                # "toodle-oo!


DBM类mix了Enumerable模块,他的类方法new和open都是singletons:
q=DBM.new("data.dbm")   #
f=DBM.open("data.dbm")  # Errno::EWOULDBLOCK:
                        #   Try again - "data.dbm"


操作dbm对象和操作hash对象差不多,想了解它的具体的方法可以去看文档.

















6
0
分享到:
评论
1 楼 cobrano1 2008-02-02  
谢谢博主,都是我想找的资料, 期待你更多的大作

相关推荐

    The Ruby Way--3rd Edition--2015-英文版

    The Ruby Way 第三版(英文版),全书22章,书中包含600多个按主题分类的示例。每个示例都回答了“如何使用Ruby来完成”的问题。 ——Ruby on Rails之父David Heinemeier Hansson倾力推荐!

    The Ruby Way(第2版)

    The Ruby Way(第2版) &lt;br&gt;The Ruby Way assumes that the reader is already familiar with the subject matter. Using many code samples it focuses on "how-to use Ruby" for specific applications, either ...

    the ruby way 2ed

    《The Ruby Way 2nd Edition》是一本深入探讨Ruby编程语言的经典著作,旨在帮助读者全面理解和掌握Ruby的精髓。这本书的第二版在2006年出版,由Addison-Wesley出版,作者通过深入浅出的方式,揭示了Ruby语言的强大...

    THE RUBY WAY(中文版)(第二版)pdf

    《The Ruby Way 第二版》...“《The Ruby Way (第2版)中文版》在阐述元编程(metaprogramming)等方面尤其出类拔萃,而元编程是Ruby最引人注目的方面之一。” ——Ruby on Rails之父David Heinemeier Hansson倾力推荐!

    the-ruby-way

    the ruby way the ruby way

    ruby学习资源(Programming Ruby, Learning Ruby, The Ruby Way)

    内含以下4个文档: 1、Addison.Wesley.The.Ruby.Way.2nd.Edition.Oct.2006.chm 2、O'Reilly.Learning.Ruby.May.2007.chm 3、Programming Ruby 2e.pdf 4、ruby中文文档.chm

    Ruby-chewy高级ElasticsearchRuby框架

    Ruby-chewy是一个高级的Elasticsearch Ruby框架,它构建在官方的elasticsearch-ruby客户端之上,为开发者提供了更简洁、强大的接口来操作Elasticsearch。这篇文章将深入探讨chewy框架的核心特性、如何安装与配置,...

    the ruby way

    通过阅读《The Ruby Way》,你不仅能学会Ruby的基础,还能掌握高级特性和最佳实践。书中的实例代码和解决问题的思路将帮助你成为一名更高效的Ruby开发者。同时,这本书也适合已经有一定编程经验的人用来提升对Ruby...

    Addison Wesley The Ruby Way 2Nd Edition Oct 2006.pdf(英文版)

    5. **高级数据结构操作**:除了基本的数据类型外,还涉及到了哈希表、数组以及其他数据结构如栈、树和图的操作。 6. **输入/输出与文件处理**:介绍了如何处理一般的输入/输出操作、文件操作以及持久化对象。 7. *...

    Ruby-Way.rar_About Language

    总之,《Ruby Way》是了解和学习Ruby语言的重要资源,它涵盖了Ruby的基本概念、核心语法、高级特性以及实际应用,对于想要进入Ruby世界的开发者来说,是一本不可多得的指南。通过深入阅读和实践书中的例子,你将能够...

    R和Ruby数据分析之旅

    R和Ruby数据分析之旅 数据分析 数据挖掘

    11.5 时间日期the ruby way.rar

    在Ruby编程语言中,时间日期处理是至关重要的,特别是在开发涉及日志记录、事件调度或数据分析的应用程序时。"11.5 时间日期the ruby way"这个主题深入探讨了Ruby中处理时间日期的最佳实践和常见用法。让我们逐一...

    R和Ruby数据分析之旅,中文完整扫描版

    如果你对万事万物的运行方式充满好奇,这本有趣的《R和Ruby数据分析之旅》会帮你找到日常生活中某些问题的真正答案。借助基本的数学方法,并使用Ruby和R语言做一些简单的编程工作,你就能学会如何对问题建模,并找出...

    The Ruby Way(处理文件和目录)

    除了基础的文件和目录操作外,Ruby还支持更高级的数据存储技术,例如对象序列化、持久化存储库等。 **对象序列化**: `Marshal`模块提供了简单对象序列化的能力,可以将对象转换为二进制格式并保存到文件中。 ```...

    Ruby Data-Processing ruby数据处理

    Ruby是一种强大的动态编程语言,尤其在数据处理方面表现出色。Map、Reduce和Select是Ruby中用于操作和处理数据的关键概念,它们在数据科学、分析和软件工程领域中扮演着重要角色。 1. **Ruby Map**: Map函数允许...

Global site tag (gtag.js) - Google Analytics