每一個(gè)不曾起舞的日子,都是對(duì)生命的辜負(fù)悼沿!
文件和異常
在實(shí)際開發(fā)中屿笼,常常需要對(duì)程序中的數(shù)據(jù)進(jìn)行持久化操作,而實(shí)現(xiàn)數(shù)據(jù)持久化最直接簡(jiǎn)單的方式就是將數(shù)據(jù)保存到文件中翁巍。說到“文件”這個(gè)詞驴一,可能需要先科普一下關(guān)于文件系統(tǒng)的知識(shí),對(duì)于這個(gè)概念灶壶,維基百科上給出了很好的詮釋肝断,這里不再浪費(fèi)筆墨。
在Python中實(shí)現(xiàn)文件的讀寫操作其實(shí)非常簡(jiǎn)單驰凛,通過Python內(nèi)置的open函數(shù)胸懈,我們可以指定文件名、操作模式恰响、編碼信息等來獲得操作文件的對(duì)象趣钱,接下來就可以對(duì)文件進(jìn)行讀寫操作了。這里所說的操作模式是指要打開什么樣的文件(字符文件還是二進(jìn)制文件)以及做什么樣的操作(讀胚宦、寫還是追加)首有,具體的如下表所示。
操作模式 | 具體含義 |
---|---|
r |
讀仁嗳啊(默認(rèn)) |
w |
寫入(會(huì)先截?cái)嘀暗膬?nèi)容) |
x |
寫入井联,如果文件已經(jīng)存在會(huì)產(chǎn)生異常 |
a |
追加,將內(nèi)容寫入到已有文件的末尾 |
b |
二進(jìn)制模式 |
t |
文本模式(默認(rèn)) |
+ |
更新(既可以讀又可以寫) |
讀寫文本文件
讀文本文件
讀取文本文件時(shí)您旁,需要在使用open
函數(shù)時(shí)指定好帶路徑的文件名(可以使用相對(duì)路徑
或絕對(duì)路徑
)并將文件模式設(shè)置為'r'
(如果不指定烙常,默認(rèn)值也是'r'),然后通過encoding
參數(shù)指定編碼(如果不指定鹤盒,默認(rèn)值是None蚕脏,那么在讀取文件時(shí)使用的是操作系統(tǒng)默認(rèn)的編碼),如果不能保證保存文件時(shí)使用的編碼方式與encoding參數(shù)指定的編碼方式是一致的昨悼,那么就可能因無法解碼字符而導(dǎo)致讀取失敗蝗锥。下面的例子演示了如何讀取一個(gè)純文本文件。
def main():
# 以'utf-8'格式打開文件
f = open('hello.txt', 'r', encoding='utf-8')
print(f.read())
# 一定記得關(guān)閉
f.close()
if __name__ == '__main__':
main()
請(qǐng)注意上面的代碼率触,如果open
函數(shù)指定的文件并不存在或者無法打開终议,那么將引發(fā)異常狀況導(dǎo)致程序崩潰。為了讓代碼有一定的健壯性和容錯(cuò)性葱蝗,我們可以使用Python的異常機(jī)制
對(duì)可能在運(yùn)行時(shí)發(fā)生狀況的代碼進(jìn)行適當(dāng)?shù)奶幚硌ㄕ牛缦滤尽?/p>
def main():
f = None
try:
f = open('hello.txt', 'r', encoding='utf-8')
print(f.read())
except FileNotFoundError as e:
print('未知文件')
except LookupError:
print('未知編碼')
except UnicodeDecodeError:
print('解碼錯(cuò)誤')
finally:
if f:
f.close()
if __name__ == '__main__':
main()
在Python中,我們可以將那些在運(yùn)行時(shí)可能會(huì)出現(xiàn)狀況的代碼放在try
代碼塊中两曼,在try代碼塊的后面可以跟上一個(gè)或多個(gè)except
來捕獲可能出現(xiàn)的異常狀況皂甘。例如在上面讀取文件的過程中,文件找不到會(huì)引發(fā)FileNotFoundError
悼凑,指定了未知的編碼會(huì)引發(fā)LookupError
偿枕,而如果讀取文件時(shí)無法按指定方式解碼會(huì)引發(fā)UnicodeDecodeError
璧瞬,我們?cè)趖ry后面跟上了三個(gè)except分別處理這三種不同的異常狀況。最后我們使用finally
代碼塊來關(guān)閉打開的文件渐夸,釋放掉程序中獲取的外部資源嗤锉,由于finally塊的代碼不論程序正常還是異常都會(huì)執(zhí)行到(甚至是調(diào)用了sys
模塊的exit
函數(shù)退出Python環(huán)境,finally塊都會(huì)被執(zhí)行墓塌,因?yàn)閑xit函數(shù)實(shí)質(zhì)上是引發(fā)了SystemExit
異常)瘟忱,因此我們通常把finally
塊稱為“總是執(zhí)行代碼塊”
,它最適合用來做釋放外部資源的操作苫幢。如果不愿意在finally代碼塊中關(guān)閉文件對(duì)象釋放資源访诱,也可以使用上下文語法,通過with
關(guān)鍵字指定文件對(duì)象的上下文環(huán)境并在離開上下文環(huán)境時(shí)自動(dòng)釋放文件資源韩肝,代碼如下所示触菜。
def main():
try:
with open('hello.txt', 'r', encoding='utf-8') as f:
print(f.read())
except FileNotFoundError:
print('無法打開指定的文件!')
except LookupError:
print('指定了未知的編碼!')
except UnicodeDecodeError:
print('讀取文件時(shí)解碼錯(cuò)誤!')
if __name__ == '__main__':
main()
除了使用文件對(duì)象的read
方法讀取文件之外,還可以使用for-in
循環(huán)逐行讀取或者用readlines
方法將文件按行讀取到一個(gè)列表容器中伞梯,代碼如下所示玫氢。
import time
def main():
# 一次性讀取整個(gè)文件內(nèi)容
with open('hello.txt', 'r', encoding='utf-8') as f:
print(f.read())
# 通過for-in循環(huán)逐行讀取
with open('hello.txt', mode='r') as f:
for line in f:
print(line, end='')
time.sleep(0.5)
print()
# 讀取文件按行讀取到列表中
with open('hello.txt') as f:
lines = f.readlines()
print(lines)
if __name__ == '__main__':
main()
讀寫文本文件
要將文本信息寫入文件文件也非常簡(jiǎn)單,在使用open
函數(shù)時(shí)指定好文件名并將文件模式設(shè)置為'w'
即可谜诫。注意如果需要對(duì)文件內(nèi)容進(jìn)行追加式寫入漾峡,應(yīng)該將模式設(shè)置為'a'
。如果要寫入的文件不存在會(huì)自動(dòng)創(chuàng)建文件而不是引發(fā)異常喻旷。下面的例子演示了如何將1-9999
直接的素?cái)?shù)分別寫入三個(gè)文件中(1-99之間的素?cái)?shù)保存在a.txt中生逸,100-999之間的素?cái)?shù)保存在b.txt中,1000-9999之間的素?cái)?shù)保存在c.txt中)且预。
from math import sqrt
def is_prime(n):
"""判斷素?cái)?shù)的函數(shù)"""
assert n > 0, 'n必須大于0'
for factor in range(2, int(sqrt(n)) + 1):
if n % factor == 0:
return False
return True if n != 1 else False
def main():
filenames = ('a.txt', 'b.txt', 'c.txt')
fs_list = []
try:
for filename in filenames:
fs_list.append(open(filename, 'w', encoding='utf-8'))
for number in range(1, 10000):
if is_prime(number):
if number < 100:
fs_list[0].write(str(number) + '\n')
elif number < 1000:
fs_list[1].write(str(number) + '\n')
else:
fs_list[2].write(str(number) + '\n')
except IOError as ex:
print(ex)
print('寫文件時(shí)發(fā)生錯(cuò)誤!')
finally:
for fs in fs_list:
fs.close()
print('操作完成!')
if __name__ == '__main__':
main()
上面代碼中槽袄,assert
作用是聲明后面的布爾值必須為真(即保證n>0
,否則會(huì)觸發(fā)異常)的判定,如果發(fā)生異常就說明表達(dá)示為假锋谐”槌撸可以理解assert斷言語句為raise-if-not
,用來測(cè)試表示式涮拗,其返回值為假乾戏,就會(huì)觸發(fā)異常。
讀寫二進(jìn)制文件
知道了如何讀寫文本文件要讀寫二進(jìn)制文件也就很簡(jiǎn)單了三热,下面的代碼實(shí)現(xiàn)了復(fù)制圖片文件的功能鼓择。
def main():
try:
# 讀取文件,'rb'
with open('haha.jpg', 'rb') as f:
data = f.read()
print(type(data)) # <class 'bytes'>
# 寫入文件,'wb'
with open('hi.jpg', 'wb') as f:
f.write(data)
except FileNotFoundError as e:
print('指定的文件無法打開.')
except IOError as e:
print('讀寫文件時(shí)出現(xiàn)錯(cuò)誤.')
print('程序執(zhí)行結(jié)束.')
if __name__ == '__main__':
main()
讀寫JSON文件
通過上面的講解就漾,我們已經(jīng)知道如何將文本數(shù)據(jù)和二進(jìn)制數(shù)據(jù)保存到文件中呐能,那么這里還有一個(gè)問題,如果希望把一個(gè)列表或者一個(gè)字典中的數(shù)據(jù)保存到文件中又該怎么做呢抑堡?答案是將數(shù)據(jù)以JSON
格式進(jìn)行保存摆出。JSON是“JavaScript Object Notation”
的縮寫朗徊,它本來是JavaScript語言中創(chuàng)建對(duì)象的一種字面量語法,現(xiàn)在已經(jīng)被廣泛的應(yīng)用于跨平臺(tái)跨語言的數(shù)據(jù)交換懊蒸,原因很簡(jiǎn)單荣倾,因?yàn)镴SON也是純文本
,任何系統(tǒng)任何編程語言處理純文本都是沒有問題的骑丸。目前JSON基本上已經(jīng)取代了XML作為異構(gòu)系統(tǒng)間交換數(shù)據(jù)的事實(shí)標(biāo)準(zhǔn)。關(guān)于JSON的知識(shí)妒貌,更多的可以參考JSON的官方網(wǎng)站通危,從這個(gè)網(wǎng)站也可以了解到每種語言處理JSON數(shù)據(jù)格式可以使用的工具或三方庫,下面是一個(gè)JSON的簡(jiǎn)單例子灌曙。
{
"name": "月上秦少",
"intro": "人生似水豈無涯菊碟。浮云吹作雪,世味煮成茶在刺。",
"job": "一個(gè)前端小菜鳥",
"web": "http://www.reibang.com/u/3075de2e101e",
"img": "xxx.jpg",
"hoby": [
{"lang": "Python"},
{"game": "lol"},
{"xx": "xx"},
{"yy": "yy"},
]
}
可能大家已經(jīng)注意到了逆害,上面的JSON跟Python中的字典其實(shí)是一樣一樣的,事實(shí)上JSON的數(shù)據(jù)類型和Python的數(shù)據(jù)類型是很容易找到對(duì)應(yīng)關(guān)系的蚣驼,如下面兩張表所示魄幕。
JSON | Python |
---|---|
object | dict |
array | list |
string | str |
number (int / real) | int / float |
true / false | True / False |
null | None |
Python | JSON |
---|---|
dict | object |
list,tuple | array |
str | string |
int,float,int-&float-derived Enums | number |
True / False | true / false |
None | null |
我們使用Python中的json模塊
就可以將字典或列表以JSON格式保存到文件中,代碼如下所示颖杏。
import json
def main():
mydict = {
"name": "月上秦少",
"intro": "人生似水豈無涯纯陨。浮云吹作雪,世味煮成茶留储。",
"job": "一個(gè)前端小菜鳥",
"web": "http://www.reibang.com/u/3075de2e101e",
"img": "xxx.jpg",
"hoby": [
{"lang": "Python"},
{"game": "lol"},
{"xx": "xx"},
{"yy": "yy"},
]
}
try:
with open('data.json', 'w', encoding='utf-8') as fs:
json.dump(mydict, fs)
except IOError as e:
print(e)
print('保存數(shù)據(jù)完成!')
if __name__ == '__main__':
main()
json模塊主要有四個(gè)比較重要的函數(shù)翼抠,分別是:
-
dump
- 將Python對(duì)象按照J(rèn)SON格式序列化到文件中 -
dumps
- 將Python對(duì)象處理成JSON格式的字符串 -
load
- 將文件中的JSON數(shù)據(jù)反序列化成對(duì)象 -
loads
- 將字符串的內(nèi)容反序列化成Python對(duì)象
這里出現(xiàn)了兩個(gè)概念,一個(gè)叫序列化
获讳,一個(gè)叫反序列化
阴颖。自由的百科全書維基百科上對(duì)這兩個(gè)概念是這樣解釋的:“序列化(serialization)
在計(jì)算機(jī)科學(xué)的數(shù)據(jù)處理中,是指將數(shù)據(jù)結(jié)構(gòu)或?qū)ο鬆顟B(tài)轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问截はィ@樣在需要的時(shí)候能夠恢復(fù)到原先的狀態(tài)量愧,而且通過序列化的數(shù)據(jù)重新獲取字節(jié)時(shí),可以利用這些字節(jié)來產(chǎn)生原始對(duì)象的副本(拷貝)尤误。與這個(gè)過程相反的動(dòng)作侠畔,即從一系列字節(jié)中提取數(shù)據(jù)結(jié)構(gòu)的操作,就是反序列化(deserialization)”损晤。
目前絕大多數(shù)網(wǎng)絡(luò)數(shù)據(jù)服務(wù)(或稱之為網(wǎng)絡(luò)API)都是基于HTTP協(xié)議提供JSON格式的數(shù)據(jù)软棺,關(guān)于HTTP協(xié)議的相關(guān)知識(shí),可以看看阮一峰老師的《HTTP協(xié)議入門》尤勋,如果想了解國(guó)內(nèi)的網(wǎng)絡(luò)數(shù)據(jù)服務(wù)喘落,可以看看聚合數(shù)據(jù)和阿凡達(dá)數(shù)據(jù)等網(wǎng)站茵宪,國(guó)外的可以看看{API}Search網(wǎng)站。下面的例子演示了如何使用requests模塊(封裝得足夠好的第三方網(wǎng)絡(luò)訪問模塊)訪問網(wǎng)絡(luò)API獲取國(guó)內(nèi)新聞瘦棋,如何通過json模塊解析JSON數(shù)據(jù)并顯示新聞標(biāo)題稀火,這個(gè)例子使用了天行數(shù)據(jù)提供的國(guó)內(nèi)新聞數(shù)據(jù)接口,其中的APIKey需要自己到該網(wǎng)站申請(qǐng)赌朋。免費(fèi)模擬API接口RandomUser – 生成隨機(jī)用戶 JSON 數(shù)據(jù)的 API
import requests
import json
def main():
resp = requests.get('http://api.tianapi.com/guonei/?key=APIKey&num=10')
data_model = json.loads(resp.text)
for news in data_model['newslist']:
print(news['title'])
if __name__ == '__main__':
main()
在Python中要實(shí)現(xiàn)序列化
和反序列化
除了使用json模塊
之外凰狞,還可以使用pickle
和shelve模塊
,但是這兩個(gè)模塊是使用特有的序列化協(xié)議
來序列化數(shù)據(jù)沛慢,因此序列化后的數(shù)據(jù)只能被Python識(shí)別赡若。關(guān)于這兩個(gè)模塊的相關(guān)知識(shí)可以自己看看網(wǎng)絡(luò)上的資料。另外团甲,如果要了解更多的關(guān)于Python異常機(jī)制的知識(shí)逾冬,可以看看segmentfault上面的文章《總結(jié):Python中的異常處理》,這篇文章不僅介紹了Python中異常機(jī)制的使用躺苦,還總結(jié)了一系列的最佳實(shí)踐身腻,很值得一讀。
exercise python 100 days from https://github.com/jackfrued/Python-100-Days