實際開發中常常會遇到對數據進行持久化操作的場景,而實現數據持久化最直接簡單的方式就是將數據儲存到檔案中。說到「檔案」這個詞,可能需要先科普一下關於檔案系統的知識,但是這裏我們並不浪費筆墨介紹這個概念,請大家自行通過維基百科進行瞭解。
在Python中實現檔案的讀寫操作其實非常簡單,通過Python內建的open
函數,我們可以指定檔名、操作模式、編碼資訊等來獲得操作檔案的物件,接下來就可以對檔案進行讀寫操作了。這裏所說的操作模式是指要開啓什麼樣的檔案(字元檔案還是二進制檔案)以及做什麼樣的操作(讀、寫還是追加),具體的如下表所示。
操作模式 | 具體含義 |
---|---|
'r' |
讀取 (預設) |
'w' |
寫入(會先截斷之前的內容) |
'x' |
寫入,如果檔案已經存在會產生異常 |
'a' |
追加,將內容寫入到已有檔案的末尾 |
'b' |
二進制模式 |
't' |
文字模式(預設) |
'+' |
更新(既可以讀又可以寫) |
下面 下麪這張圖來自於菜鳥教學網站,它展示瞭如果根據應用程式的需要來設定操作模式。
[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-1z3us6yS-1597320991488)(./res/file-open-mode.png)]
讀取文字檔案時,需要在使用open
函數時指定好帶路徑的檔名(可以使用相對路徑或絕對路徑)並將檔案模式設定爲'r'
(如果不指定,預設值也是'r'
),然後通過encoding
參數指定編碼(如果不指定,預設值是None,那麼在讀取檔案時使用的是操作系統預設的編碼),如果不能保證儲存檔案時使用的編碼方式與encoding參數指定的編碼方式是一致的,那麼就可能因無法解碼字元而導致讀取失敗。下面 下麪的例子演示瞭如何讀取一個純文字檔案。
def main():
f = open('致橡樹.txt', 'r', encoding='utf-8')
print(f.read())
f.close()
if __name__ == '__main__':
main()
請注意上面的程式碼,如果open
函數指定的檔案並不存在或者無法開啓,那麼將引發異常狀況導致程式崩潰。爲了讓程式碼有一定的健壯性和容錯性,我們可以使用Python的異常機制 機製對可能在執行時發生狀況的程式碼進行適當的處理,如下所示。
def main():
f = None
try:
f = open('致橡樹.txt', 'r', encoding='utf-8')
print(f.read())
except FileNotFoundError:
print('無法開啓指定的檔案!')
except LookupError:
print('指定了未知的編碼!')
except UnicodeDecodeError:
print('讀取檔案時解碼錯誤!')
finally:
if f:
f.close()
if __name__ == '__main__':
main()
在Python中,我們可以將那些在執行時可能會出現狀況的程式碼放在try
程式碼塊中,在try
程式碼塊的後面可以跟上一個或多個except
來捕獲可能出現的異常狀況。例如在上面讀取檔案的過程中,檔案找不到會引發FileNotFoundError
,指定了未知的編碼會引發LookupError
,而如果讀取檔案時無法按指定方式解碼會引發UnicodeDecodeError
,我們在try
後面跟上了三個except
分別處理這三種不同的異常狀況。最後我們使用finally
程式碼塊來關閉開啓的檔案,釋放掉程式中獲取的外部資源,由於finally
塊的程式碼不論程式正常還是異常都會執行到(甚至是呼叫了sys
模組的exit
函數退出Python環境,finally
塊都會被執行,因爲exit
函數實質上是引發了SystemExit
異常),因此我們通常把finally
塊稱爲「總是執行程式碼塊」,它最適合用來做釋放外部資源的操作。如果不願意在finally
程式碼塊中關閉檔案物件釋放資源,也可以使用上下文語法,通過with
關鍵字指定檔案物件的上下文環境並在離開上下文環境時自動釋放檔案資源,程式碼如下所示。
def main():
try:
with open('致橡樹.txt', 'r', encoding='utf-8') as f:
print(f.read())
except FileNotFoundError:
print('無法開啓指定的檔案!')
except LookupError:
print('指定了未知的編碼!')
except UnicodeDecodeError:
print('讀取檔案時解碼錯誤!')
if __name__ == '__main__':
main()
除了使用檔案物件的read
方法讀取檔案之外,還可以使用for-in
回圈逐行讀取或者用readlines
方法將檔案按行讀取到一個列表容器中,程式碼如下所示。
import time
def main():
# 一次性讀取整個檔案內容
with open('致橡樹.txt', 'r', encoding='utf-8') as f:
print(f.read())
# 通過for-in回圈逐行讀取
with open('致橡樹.txt', mode='r') as f:
for line in f:
print(line, end='')
time.sleep(0.5)
print()
# 讀取檔案按行讀取到列表中
with open('致橡樹.txt') as f:
lines = f.readlines()
print(lines)
if __name__ == '__main__':
main()
要將文字資訊寫入檔案檔案也非常簡單,在使用open
函數時指定好檔名並將檔案模式設定爲'w'
即可。注意如果需要對檔案內容進行追加式寫入,應該將模式設定爲'a'
。如果要寫入的檔案不存在會自動建立檔案而不是引發異常。下面 下麪的例子演示瞭如何將1-9999之間的素數分別寫入三個檔案中(1-99之間的素數儲存在a.txt中,100-999之間的素數儲存在b.txt中,1000-9999之間的素數儲存在c.txt中)。
from math import sqrt
def is_prime(n):
"""判斷素數的函數"""
assert 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('寫檔案時發生錯誤!')
finally:
for fs in fs_list:
fs.close()
print('操作完成!')
if __name__ == '__main__':
main()
知道瞭如何讀寫文字檔案要讀寫二進制檔案也就很簡單了,下面 下麪的程式碼實現了複製圖片檔案的功能。
def main():
try:
with open('guido.jpg', 'rb') as fs1:
data = fs1.read()
print(type(data)) # <class 'bytes'>
with open('吉多.jpg', 'wb') as fs2:
fs2.write(data)
except FileNotFoundError as e:
print('指定的檔案無法開啓.')
except IOError as e:
print('讀寫檔案時出現錯誤.')
print('程式執行結束.')
if __name__ == '__main__':
main()
通過上面的講解,我們已經知道如何將文字數據和二進制數據儲存到檔案中,那麼這裏還有一個問題,如果希望把一個列表或者一個字典中的數據儲存到檔案中又該怎麼做呢?答案是將數據以JSON格式進行儲存。JSON是「JavaScript Object Notation」的縮寫,它本來是JavaScript語言中建立物件的一種字面量語法,現在已經被廣泛的應用於跨平臺跨語言的數據交換,原因很簡單,因爲JSON也是純文字,任何系統任何程式語言處理純文字都是沒有問題的。目前JSON基本上已經取代了XML作爲異構系統間交換數據的事實標準。關於JSON的知識,更多的可以參考JSON的官方網站,從這個網站也可以瞭解到每種語言處理JSON數據格式可以使用的工具或三方庫,下面 下麪是一個JSON的簡單例子。
{
"name": "駱昊",
"age": 38,
"qq": 957658,
"friends": ["王大錘", "白元芳"],
"cars": [
{"brand": "BYD", "max_speed": 180},
{"brand": "Audi", "max_speed": 280},
{"brand": "Benz", "max_speed": 320}
]
}
可能大家已經注意到了,上面的JSON跟Python中的字典其實是一樣一樣的,事實上JSON的數據型別和Python的數據型別是很容易找到對應關係的,如下面 下麪兩張表所示。
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': '駱昊',
'age': 38,
'qq': 957658,
'friends': ['王大錘', '白元芳'],
'cars': [
{'brand': 'BYD', 'max_speed': 180},
{'brand': 'Audi', 'max_speed': 280},
{'brand': 'Benz', 'max_speed': 320}
]
}
try:
with open('data.json', 'w', encoding='utf-8') as fs:
json.dump(mydict, fs)
except IOError as e:
print(e)
print('儲存數據完成!')
if __name__ == '__main__':
main()
json模組主要有四個比較重要的函數,分別是:
dump
- 將Python物件按照JSON格式序列化到檔案中dumps
- 將Python物件處理成JSON格式的字串load
- 將檔案中的JSON數據反序列化成物件loads
- 將字串的內容反序列化成Python物件這裏出現了兩個概念,一個叫序列化,一個叫反序列化。自由的百科全書維基百科上對這兩個概念是這樣解釋的:「序列化(serialization)在電腦科學的數據處理中,是指將數據結構或物件狀態轉換爲可以儲存或傳輸的形式,這樣在需要的時候能夠恢復到原先的狀態,而且通過序列化的數據重新獲取位元組時,可以利用這些位元組來產生原始物件的副本(拷貝)。與這個過程相反的動作,即從一系列位元組中提取數據結構的操作,就是反序列化(deserialization)」。
目前絕大多數網路數據服務(或稱之爲網路API)都是基於HTTP協定提供JSON格式的數據,關於HTTP協定的相關知識,可以看看阮一峯老師的《HTTP協定入門》,如果想瞭解國內的網路數據服務,可以看看聚合數據和阿凡達數據等網站,國外的可以看看{API}Search網站。下面 下麪的例子演示瞭如何使用requests模組(封裝得足夠好的第三方網路存取模組)存取網路API獲取國內新聞,如何通過json模組解析JSON數據並顯示新聞標題,這個例子使用了天行數據提供的國內新聞數據介面,其中的APIKey需要自己到該網站申請。
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中要實現序列化和反序列化除了使用json模組之外,還可以使用pickle和shelve模組,但是這兩個模組是使用特有的序列化協定來序列化數據,因此序列化後的數據只能被Python識別。關於這兩個模組的相關知識可以自己看看網路上的資料。另外,如果要瞭解更多的關於Python異常機制 機製的知識,可以看看segmentfault上面的文章《總結:Python中的例外處理》,這篇文章不僅介紹了Python中異常機制 機製的使用,還總結了一系列的最佳實踐,很值得一讀。