1.4.1.15. fejezet, Marshal avagy objektumok tárolása másként
Hasonlítsuk össze az alábbi kódot az előző fejezetben tanultakkal:
f = File.open( 'friends.sav', 'w' ) Marshal.dump( ["fred", "bert", "mary"], f ) f.close File.open( 'morefriends.sav', 'w' ){ |friendsfile| Marshal.dump( ["sally", "agnes", "john" ], friendsfile ) } File.open( 'morefriends.sav' ){ |f| $arr = Marshal.load(f) } myfriends = Marshal.load(File.open( 'friends.sav' )) morefriends = Marshal.load(File.open( 'morefriends.sav' )) p( myfriends ) p( morefriends ) p( $arr )
Ugye, hogy mennyire hasonló? Csak a YAML helyére Marshal-t írtunk. Az eredménye pedig itt van:
["fred", "bert", "mary"] ["sally", "agnes", "john"] ["sally", "agnes", "john"]
Ha belenézünk egy fájlba, pl.: friends.sav, látni fogjuk, hogy a YAML-el szemben ez egy bináris állomány. Néhány szövegrész olvasható, de nem lenne egyszerű egy szövegszerkesztővel módosítani. Ugyan olyan könnyű sorozatokat gyártani akármelyik adattípusból és adatstruktúrákból a legfelső szintű objektum mentésével, mint YAML-ben. A különbség az IO osztály használatakor (példányosításakor) és az objektum-egyedi metódusok mentésekor keletkező TypeError kivételben érhető tetten. Megnézzük ezt az objektum-egyedi metódusoknál.
Változók elhagyása mentéskor
Mint a YAML-nél, itt is meghatározható, melyik tulajdonság kerüljön ki mentéskor. A marshal_dump metódus felülírása szükséges ehhez:
def marshal_dump [@num, @arr] end
A marshal-nál még egy metódust felül kell írjunk. Ez a marshal_load, ami az objektum tulajdonságait visszatölti az újonnan létrehozott objektumba:
class Mclass def initialize(aNum, aStr, anArray) @num = aNum @str = aStr @arr = anArray end def marshal_dump [@num, @arr] end def marshal_load(data) @num = data[0] @arr = data[1] @str = "default string" end end ob = Mclass.new( 100, "fred", [1,2,3] ) p( ob ) marshal_data = Marshal.dump( ob ) ob2 = Marshal.load( marshal_data ) p( ob2 )
Eredménye:
#<Mclass:0x9005524 @num=100, @str="fred", @arr=[1, 2, 3]> #<Mclass:0x90053bc @num=100, @str="default string", @arr=[1, 2, 3]>
Megjegyzem: ahogy itt a memóriában, a lemezen is működik ugyan ezzel a technikával.
Egyedi objektum mentése
Marshal módon egyedi objektumot nem lehet menteni, kivételt generál a mentés.
ob = Object.new class << ob def xxx( aStr ) @x = aStr end end ob.xxx( "hello" ) p( ob ) File.open( 'test.sav', 'w' ){ |f| Marshal.dump( ob, f ) } File.open( 'test.sav' ){ |f| ob = Marshal.load(f) } p( ob )
Eredménye:
#<Object:0x8ccc7e0 @x="hello"> singleton_m.rb:12:in `dump': singleton can't be dumped (TypeError) singleton_m.rb:12:in `block in <main>' singleton_m.rb:11:in `open' singleton_m.rb:11:in `<main>'
Ugyanez YAML-ben félig működik.
require 'yaml' ob = Object.new # singleton class class << ob def xxx( aStr ) @x = aStr end end ob.xxx( "hello world" ) p( ob ) File.open( 'test.yml', 'w' ){ |f| YAML.dump( ob, f ) } ob.xxx( "new string" ) p( ob ) File.open( 'test.yml' ){ |f| ob = YAML.load(f) } p( ob )
Eredménye:
#<Object:0x8f204d8 @x="hello world"> #<Object:0x8f204d8 @x="new string"> #<Object:0x8f1b7e4 @x="hello world">
Ha megnézzük a YAML fájlt, ezt látjuk:
--- !ruby/object x: hello world
Ha megnézzük az futási eredményt, azt látjuk, hogy van ugyan egy x példány változónk, de nincs meg az egyedi objektumban definiált xxx metódusunk. Tehát habár a YAML jóval engedékenyebb az adat mentésnél és betöltésnél, nem hozza teljesen létre az egyedi objektumot amikor visszatölti az adatokat.
Ami működik Marshal módon, az kicsit bonyolultabb:
FILENAME = 'test2.sav' class X def marshal_dump [@x] end def marshal_load(data) @x = data[0] end end def makeIntoSingleton( someOb ) class << someOb def xxx=( aStr ) @x = aStr end def xxx return @x end end return someOb end ob = X.new if File.exists?(FILENAME) then File.open(FILENAME){ |f| ob = Marshal.load(f) } else puts( "Saved data can't be found" ) end p( ob ) ob = makeIntoSingleton( ob ) if ob.xxx == "hello" then ob.xxx = "goodbye" else ob.xxx = "hello" end File.open( FILENAME, 'w' ){ |f| Marshal.dump( ob, f ) } p( ob ) puts( ob.xxx )
Az eredmény pedig:
#<X:0x84305b4 @x="hello"> #<X:0x84305b4 @x="goodbye"> goodbye
Még egy összehasonlítást nézzünk meg. Az elmentett dump verziója jó, ha megegyezik az aktuálisan használttal, különben bonyodalmak keletkezhetnek, amik kivételeket generálhatnak. Hasonlítsuk össze a jelenleg használtat a fájlban tárolttal. Hogy hol tárolja a fájlt? Az első két bájtban. Ez is egy kis körüljárást igényel, ugyanis 1.8-ban a File.getc() fixnum-ot ad vissza, 1.9-ben már 1 byte hosszú stringet. Tehát a getc egyszerűbb lenne, de TypeError kivétel keletkezik, ha használjuk. Az interneten azt találtam, hogy a String osztálynak van egy unpack metódusa, amivel byte hosszú String értéket alakíthatok számmá. Nézzük hogy sikerült:
f = File.open('test2.sav') vMajor = f.read(1).unpack('c').join().to_i #getc helyett vMinor = f.read(1).unpack('c').join().to_i #getc helyett f.close puts("File: Marshal Version=#{vMajor}:#{vMinor}" ) puts("Current Marshal Version=#{Marshal::MAJOR_VERSION}:#{Marshal::MINOR_VERSION}" ) if vMajor == Marshal::MAJOR_VERSION then puts( "Major version number is compatible" ) if vMinor == Marshal::MINOR_VERSION then puts( "Minor version number is compatible" ) elsif vMinor < Marshal::MINOR_VERSION then puts( "Minor version is lower - old file format" ) else puts( "Minor version is higher - newer file format" ) end else puts( "Major version number is incompatible" ) end
És az eredmény:
File: Marshal Version=4:8 Current Marshal Version=4:8 Major version number is compatible Minor version number is compatible
Ez visszafelé is kompatibilis.
- A hozzászóláshoz be kell jelentkezni