《Python程式設計:從入門到實踐》筆記。
本章主要是學習Python的檔案操作,主要是從檔案中讀取資料以及將資料儲存到檔案中,還有錯誤處理,異常類,json模組等。
1. 從檔案中讀資料
1.1 讀取整個檔案
以下檔案pi_digits.txt
包含了精確到小數點後30位的圓周率資料
# pi_digits.txt檔案
3.1415926535
8979323846
2643383279
# 程式碼:
with open("pi_digits.txt", "r") as file_object:
contents = file_object.read() # 一次性讀取整個檔案
print(contents)
# 結果:和上述檔案內容一樣
複製程式碼
從上述程式碼可以看出,我們開啟檔案使用open()
函式,該函式至少接收一個引數,即檔案路徑。讀取檔案時需要向open()
函式指明是用什麼方式讀取檔案,是隻讀("r"
),只寫("w"
),末尾新增("a"
)還是讀寫均可("r+"
),open()
函式預設以“只讀”方式讀取檔案。這只是4中常用的檔案讀取方式,此外還有至少8種讀寫方式。open()
函式返回一個檔案物件,file_object
用於接收該物件。通過檔案物件的read()
方法讀取檔案內容,且該方法返回整個檔案的內容。
上述程式碼中的檔案和原始碼在同一目錄中。注意檔案路徑的問題,絕對路徑(不提倡)和相對路徑(相對於原始檔的路徑)以及Windows和Linux下路徑的寫法。
注意程式碼中的with
關鍵字。其實讀寫檔案不需要該關鍵字,開啟檔案使用open()
函式,檔案讀取完後關閉檔案使用close()
函式,讀取內容可以呼叫read()
方法。而之所以使用with
關鍵字,主要是因為①你最後忘記關閉檔案,就想忘了關燈一樣;②也可能是在關閉前程式出錯,導致close()
語句未執行。這些讓檔案沒有關閉的情況都有可能導致資料丟失或損壞。with
關鍵字則被用來應對這些情況,它保證在結束with
塊時,檔案一定會被關閉。
1.2 逐行讀取
上述程式碼一次性讀取整個檔案,這在檔案較小或者記憶體充裕的時候沒有問題,但如果檔案特別大,記憶體容量又很羞澀,則只能逐行讀取:
# 程式碼:
file_name = "pi_digits.txt"
with open(file_name) as file_pi:
for line in file_pi: # 也可以通過while迴圈配合readline()方法逐行讀取檔案
print(line)
# 結果:
3.1415926535
8979323846
2643383279
複製程式碼
這裡需要注意一個問題,就是對行以及檔案末尾空字元的讀取問題,read()
和readline()
方法會讀取末尾的空字元(這裡是換行符)。我們可以通過之前講的rstrip()
方法去掉末尾的空字元。
1.3 將檔案每一行放入列表中
readlines()
方法將檔案中每一行存入列表並返回,以下程式碼進一步處理檔案中的內容:
# 程式碼:
file_name = "pi_digits.txt"
with open(file_name) as file_pi:
lines = file_pi.readlines()
pi_string = ""
for line in lines:
pi_string += line.strip()
print(pi_string)
print(len(pi_string))
# 結果:
3.141592653589793238462643383279
32
複製程式碼
注意,Python從檔案中讀取出的所有內容都是字串,如果你想要的是數字,請記得轉換。
2. 寫入檔案
以下是一個簡單的檔案寫入程式:
filename = "python.txt"
with open(filename, "w") as file_obj:
file_obj.write("I love python!")
複製程式碼
執行改程式碼後你會看到在同一目錄下會生成一個名為“python.txt"的檔案。需要注意的是,以"w"
方式開啟檔案,如果要寫入的檔案不存在,則會自動建立該檔案;如果該檔案存在,該檔案的內容會被清空,然後再寫入。如果不想檔案被清空,請使用"a"
(檔案指標放在檔案末尾)或"r+"
(檔案指標指向檔案開頭)方式開啟檔案。還有一點,write()
函式不會在檔案末尾新增換行符,如果需要換行符,請自行新增。
3. 異常
Python中使用被稱為異常的特殊物件來管理程式執行期間發生的錯誤。每當程式碼執行時如果遇到了不能處理的錯誤,Python都會建立一個異常物件,如果程式中沒有處理該物件的相關程式碼,程式將會停止,並顯示一個traceback
,其中包含異常的相關報告。如果不想程式因為某些異常而終止執行,則需要我們使用try-except
程式碼塊自行處理異常。以下是一個處理除零錯誤ZeroDivisionError
的例子:
# 程式碼:
# 捕捉異常
try:
resule = 5 / 0
except ZeroDivisionError:
print("You can't divide by zero!\n")
# 不捕捉異常
print(5 / 0)
# 結果:
You can't divide by zero!
Traceback (most recent call last):
File "division.py", line 29, in <module>
print(5 / 0)
ZeroDivisionError: division by zero
複製程式碼
如果你打算編寫一個計算器應用,那麼這段程式碼必不可少。第一個例子表明,即使發生了異常,只要異常被我們捕捉,那麼程式便不會終止。如果只想捕捉異常,但暫時又不想處理,可以將上述的print("You can't divide by zero!\n")
替換為pass
語句。如果想捕獲所有的異常,則except
後面不指定異常型別。
3.1 else程式碼塊
try-except
程式碼塊還可以和else語句組合形成try-except-else
程式碼塊,該結構表示,如果捕獲了異常,這執行except
中的程式,沒有發生異常則執行else
中的程式。以下程式是一個迴圈統計檔案中單詞數的例子,檔案讀取的部分被放到了函式中,該函式檢測有沒有發生FileNotFoundError
:
# 程式碼:
def count_words(filename):
"""計算一個檔案大致包含多少個單詞"""
try:
with open(filename) as f_obj:
contents = f_obj.read()
except FileNotFoundError:
msg = "Sorry, the file" + filename + " does not exist."
print(msg)
else:
# 計算檔案大隻包含多少個單詞
words = contents.split()
num_words = len(words)
print("The file " + filename + " has about " + str(num_words) + "words.")
filenames = ["alice.txt", "siddhartha.txt", "moby_dick.txt", "little_women.txt"]
for filename in filenames:
count_words(filename)
# 結果:
The file alice.txt has about 29461 words.
Sorry, the file siddhartha.txt does not exist.
The file moby_dick.txt has about 215136 words.
The file little_women.txt has about 189097 words.
複製程式碼
對else
的補充: 其實else
不光可以和if
,try-except
結合,還可以和for
迴圈和while
迴圈結合,比如:
for i in range(10):
pass
else:
pass
i = 0
while i < 10:
i++
else:
pass
複製程式碼
這裡的else
表示當迴圈結束後執行一些語句,比如提示之類的。
3.2 決定報告哪些錯誤
編寫得很好且經過詳盡測試的程式碼不容易出現內部錯誤,如語法或邏輯錯誤,但只要程式依賴於外部因素,如使用者輸入、存在指定的檔案、有網路連線等,就有可能出現異常。憑經驗可判斷改在程式的什麼地方包含異常處理塊,以及出現錯誤時該向使用者提供多少相關的資訊。
4. 儲存資料
很多程式要求使用者輸入某種資訊,也有可能程式中某些變數的資料在程式結束後不能丟失(比如機器學習最後訓練出來的模型引數),這是就需要將這些資訊以檔案的形式存下來。儲存資料的方式有很多,現在比較簡單且通用的是使用json
來儲存資訊。json
(JavaScript Object Notation)格式最初是為JavaScript 開發的,但隨後成了一種常見格式,並被包括Python在內的眾多語言採用。以下是一個經過了重構的儲存使用者資訊的例子:
import json
def get_stored_username(filename):
"""如果儲存了使用者名稱,就獲取它"""
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
return None
else:
return username
def get_new_username(filename):
"""提示使用者輸入使用者名稱,並存入檔案"""
username = input("What's your name?")
with open(filename, "w") as f_obj:
json.dump(username, f_obj)
return username
def greet_user(filename):
"""向使用者打招呼"""
username = get_stored_username(filename)
if username:
print("Welcome back, " + username + "!")
else:
username = get_new_username(filename)
print("We'll remember you when you come back, " + username + "!")
filename = "username.json"
greet_user(filename)
複製程式碼
程式碼就不執行了,請各位自行推導程式的結果。最後在username.json
檔案中會存有使用者的資訊。但要注意一點,json
根據資料型別來儲存資料,雖然最後都是字串,但這個過程不需要我們干預,比如要存一個列表,並不需要我們先將其轉換為字串,再存入json
,讀取資料時也不需要我們先讀取為字串,再轉換成列表,我們只需直接存取即可,轉換工作由json
模組自動完成。
迎大家關注我的微信公眾號"程式碼港" & 個人網站 www.vpointer.net ~