將數(shù)據(jù)存入文件

2018-02-24 15:48 更新

《文件(1)》《文件(2)》中,已經(jīng)學(xué)習(xí)了如何讀寫文件。

如果在程序中,有數(shù)據(jù)要保存到磁盤中,放到某個文件中是一種不錯的方法。但是,如果像以前那樣存,未免有點凌亂,并且沒有什么良好的存儲格式,導(dǎo)致數(shù)據(jù)以后被讀出來的時候遇到麻煩,特別是不能讓另外的使用者很好地理解。不要忘記了,編程是一個合作的活。還有,存儲的數(shù)據(jù)不一定都是類似字符串、整數(shù)那種基礎(chǔ)類型的。

總而言之,需要將要存儲的對象格式化(或者叫做序列化),才好存好取。這就有點類似集裝箱的作用。

所以,要用到本講中提供的方式。

pickle

pickle是標準庫中的一個模塊,還有跟它完全一樣的叫做cpickle,兩者的區(qū)別就是后者更快。所以,下面操作中,不管是用import pickle,還是用import cpickle as pickle,在功能上都是一樣的。

>>> import pickle
>>> integers = [1, 2, 3, 4, 5]
>>> f = open("22901.dat", "wb")
>>> pickle.dump(integers, f)
>>> f.close()

pickle.dump(integers, f)將數(shù)據(jù)integers保存到了文件22901.dat中。如果你要打開這個文件,看里面的內(nèi)容,可能有點失望,但是,它對計算機是友好的。這個步驟,可以稱之為將對象序列化。用到的方法是:

pickle.dump(obj,file[,protocol])

  • obj:序列化對象,上面的例子中是一個列表,它是基本類型,也可以序列化自己定義的類型。
  • file:一般情況下是要寫入的文件。更廣泛地可以理解為為擁有write()方法的對象,并且能接受字符串為為參數(shù),所以,它還可以是一個StringIO對象,或者其它自定義滿足條件的對象。
  • protocol:可選項。默認為False(或者說0),是以ASCII格式保存對象;如果設(shè)置為1或者True,則以壓縮的二進制格式保存對象。

下面換一種數(shù)據(jù)格式,并且做對比:

>>> import pickle
>>> d = {}
>>> integers = range(9999)
>>> d["i"] = integers        #下面將這個dict格式的對象存入文件

>>> f = open("22902.dat", "wb")
>>> pickle.dump(d, f)           #文件中以ascii格式保存數(shù)據(jù)
>>> f.close()

>>> f = open("22903.dat", "wb")
>>> pickle.dump(d, f, True)     #文件中以二進制格式保存數(shù)據(jù)
>>> f.close()

>>> import os
>>> s1 = os.stat("22902.dat").st_size    #得到兩個文件的大小
>>> s2 = os.stat("22903.dat").st_size

>>> print "%d, %d, %.2f%%" % (s1, s2, (s2+0.0)/s1*100)
68903, 29774, 43.21%

比較結(jié)果發(fā)現(xiàn),以二進制方式保存的文件比以ascii格式保存的文件小很多,前者約是后者的43%。

所以,在序列化的時候,特別是面對較大對象時,建議將dump()的參數(shù)True設(shè)置上,雖然現(xiàn)在存儲設(shè)備的價格便宜,但是能省還是省點比較好。

存入文件,僅是一個目標,還有另外一個目標,就是要讀出來,也稱之為反序列化。

>>> integers = pickle.load(open("22901.dat", "rb"))
>>> print integers
[1, 2, 3, 4, 5]

就是前面存入的那個列表。再看看被以二進制存入的那個文件:

>>> f = open("22903.dat", "rb")
>>> d = pickle.load(f)
>>> print d
{'i': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, ....   #省略后面的數(shù)字}
>>> f.close()

還是有自己定義數(shù)據(jù)類型的需要,這種類型是否可以用上述方式存入文件并讀出來呢?看下面的例子:

>>> import cPickle as pickle        #cPickle更快
>>> import StringIO                 #標準庫中的一個模塊,跟file功能類似,只不過是在內(nèi)存中操作“文件”

>>> class Book(object):             #自定義一種類型
...     def __init__(self,name):
...         self.name = name
...     def my_book(self):
...         print "my book is: ", self.name
... 

>>> pybook = Book("<from beginner to master>")
>>> pybook.my_book()
my book is:  <from beginner to master>

>>> file = StringIO.StringIO()
>>> pickle.dump(pybook, file, 1)
>>> print file.getvalue()           #查看“文件”內(nèi)容,注意下面不是亂碼
ccopy_reg
_reconstructor
q?(c__main__
Book
q?c__builtin__
object
q?NtRq?}qU?nameq?U?<from beginner to master>sb.

>>> pickle.dump(pybook, file)       #換一種方式,再看內(nèi)容,可以比較一下
>>> print file.getvalue()           #視覺上,兩者就有很大差異
ccopy_reg
_reconstructor
q?(c__main__
Book
q?c__builtin__
object
q?NtRq?}qU?nameq?U?<from beginner to master>sb.ccopy_reg
_reconstructor
p1
(c__main__
Book
p2
c__builtin__
object
p3
NtRp4
(dp5
S'name'
p6
S'<from beginner to master>'
p7
sb.

如果要從文件中讀出來:

>>> file.seek(0)       #找到對應(yīng)類型  
>>> pybook2 = pickle.load(file)
>>> pybook2.my_book()
my book is:  <from beginner to master>
>>> file.close()

shelve

pickle模塊已經(jīng)表現(xiàn)出它足夠好的一面了。不過,由于數(shù)據(jù)的復(fù)雜性,pickle只能完成一部分工作,在另外更復(fù)雜的情況下,它就稍顯麻煩了。于是,又有了shelve。

shelve模塊也是標準庫中的。先看一下基本操作:寫入和讀取

>>> import shelve
>>> s = shelve.open("22901.db")
>>> s["name"] = "www.itdiffer.com"
>>> s["lang"] = "python"
>>> s["pages"] = 1000
>>> s["contents"] = {"first":"base knowledge","second":"day day up"}
>>> s.close()

以上完成了數(shù)據(jù)寫入的過程。其實,這更接近數(shù)據(jù)庫的樣式了。下面是讀取。

>>> s = shelve.open("22901.db")
>>> name = s["name"]
>>> print name
www.itdiffer.com
>>> contents = s["contents"]
>>> print contents
{'second': 'day day up', 'first': 'base knowledge'}

當然,也可以用for語句來讀:

>>> for k in s:
...     print k, s[k]
... 
contents {'second': 'day day up', 'first': 'base knowledge'}
lang python
pages 1000
name www.itdiffer.com

不管是寫,還是讀,都似乎要簡化了。所建立的對象s,就如同字典一樣,可稱之為類字典對象。所以,可以如同操作字典那樣來操作它。

但是,要小心坑:

>>> f = shelve.open("22901.db")
>>> f["author"]
['qiwsir']
>>> f["author"].append("Hetz")    #試圖增加一個
>>> f["author"]                   #坑就在這里
['qiwsir']
>>> f.close()

當試圖修改一個已有鍵的值時,沒有報錯,但是并沒有修改成功。要填平這個坑,需要這樣做:

>>> f = shelve.open("22901.db", writeback=True)    #多一個參數(shù)True
>>> f["author"].append("Hetz")
>>> f["author"]                   #沒有坑了
['qiwsir', 'Hetz']
>>> f.close()

還用for循環(huán)一下:

>>> f = shelve.open("22901.db")
>>> for k,v in f.items():
...     print k,": ",v
... 
contents :  {'second': 'day day up', 'first': 'base knowledge'}
lang :  python
pages :  1000
author :  ['qiwsir', 'Hetz']
name :  www.itdiffer.com

shelve更像數(shù)據(jù)庫了。

不過,它還不是真正的數(shù)據(jù)庫。真正的數(shù)據(jù)庫在后面。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號