《Python程式設計:從入門到實踐》第十章:檔案和異常
10.1 從檔案中讀取資料
每當需要分析或修改儲存在檔案中的資訊的時候,讀取檔案很有用。
10.1.1 讀取整個檔案
用記事本編寫檔案pi_digits.txt
在file_reader.py中編寫
with open('pi_digits.txt') as file_object:
contents = file_object.read()
print(contents)
函式open()接受一個引數:要開啟的檔案的名稱,函式open()返回一個表示檔案的物件。Python將這個物件儲存在我們將在後面使用的變數中。
Python在當前執行的執行的.py檔案所在的目錄中查詢指定的檔案。
關鍵字with在不再需要訪問檔案後將其關閉。也可以
使用方法read()讀取檔案的內容,儲存在變數contents中。
但是該輸出尾部多了一個空行,因為read()到達檔案末尾時返回一個空字串,而將這個空字串顯示出來時就是一個空行。要刪除多出來的空行,可在print語句中使用rstrip()
print(contents.rstrip())
10.1.2 檔案路徑
要讓Python開啟不與程式檔案位於同一個目錄中的檔案,需要提供檔案路徑,它讓Python到系統的特定位置去查詢。
相對檔案路徑:
Windows系統中,在檔案路徑中使用反斜槓(\)而不是斜槓(/):
with open('text_files\filename.txt') as file_object:
絕對檔案路徑:
絕對路徑通常比相對路徑更長,因此將其儲存在一個變數中,再將該變數傳遞給open()會有
所幫助。
而在Windows系統中,它們類似於下面這樣:
file_path = 'C:\Users\ehmatthes\other_files\text_files\filename.txt'
with open(file_path) as file_object:
10.1.3 逐行讀取
要以每次一行的方式檢查檔案,可對檔案物件使用for迴圈:
我們將要讀取的檔案的名稱儲存在變數filename中,這是使用檔案時一種常見的做法。
#10.1.3 逐行讀取檔案內容,可對檔案進行for迴圈。。。
filename = 'pi_digits.txt'
with open(filename) as file_object:
for line in file_object:
print(line)
但是會發現有很多的空白行,這是因為在這個檔案中,每行的末尾都有一個看不見的換行符,而print語句也會加上一個換行符,因此每行末尾都有兩個換行符:一個來自檔案,另一個來自print語句。要消除這些多餘的空白行,可在print語句中使用rstrip()
10.1.4 建立一個包含檔案各行內容的列表
使用關鍵字with時,open()返回的檔案物件只在with程式碼塊內可用。如果要在with程式碼塊外
訪問檔案的內容,可在with程式碼塊內將檔案的各行儲存在一個列表中,並在with程式碼塊外使用該列表:
#10.1.4 建立一個包含檔案各行內容的列表
filename = 'pi_digits.txt'
with open(filename) as file_object:
lines = file_object.readlines()
for line in lines:
print(line.rstrip())
方法readlines()從檔案中讀取每一行,並將其儲存在一個列表中,列表lines每個元素都對應於檔案中的一行。
10.1.5 使用檔案的內容
將檔案讀取到記憶體中後,就可以以任何方式使用這些資料了。
filename = 'pi_digits.txt'
with open(filename) as file_object:
lines = file_object.readlines()
pi_string = ''
for line in lines:
pi_string += line.strip()
print(pi_string)
print(len(pi_string))
變數pi_string,用於儲存圓周率的值,接下來,我們使用一個迴圈將各行都加入pi_string,並刪除每行末尾的換行符。在變數pi_string儲存的字串中,包含原來位於每行左邊的空格,為刪除這些空格,可使用strip()而不是rstrip()。
讀取文字檔案時,Python將其中的所有文字都解讀為字串。如果你讀取的是數字,並
要將其作為數值使用,就必須使用函式int()將其轉換為整數,或使用函式float()將其轉換為浮點數。
10.1.6 包含一百萬位的大型檔案
10.1.7 圓周率值中包含你的生日嗎
birthday = input("Enter your birthday, in the form mmddyy: ")
if birthday in pi_string:
print("Your birthday appears in the first million digits of pi!")
else:
print("Your birthday does not appear in the first million digits of pi.")
10.2 寫入檔案
10.2.1 寫入空檔案
要將文字寫入檔案,你在呼叫open()時需要提供另一個實參,告訴Python你要寫入開啟的文
件。open()第一個實參也是要開啟的檔案的名稱;第二個實參(‘w’)告訴Python,我們要以寫入模式開啟這個檔案。
# 10.2.1 寫入空檔案
filename = 'programming.txt'
with open(filename, 'w') as file_object:
file_object.write("I love programming.")
開啟檔案時,可指定讀取模式(‘r’)、寫入模式(‘w’)、附加模式(‘a’)或讓你能夠讀取和寫入檔案的模式(‘r+’)。如果你省略了模式實參,Python將以預設的只讀模式開啟檔案。
如果要寫入的檔案不存在,python將會自動建立它。
以寫入(‘w’)模式開啟檔案時千萬要小心,因為如果指定的檔案已經存在,Python將在返回檔案物件前清空該檔案。
使用檔案物件的**方法write()**將一個字串寫入檔案。
Python只能將字串寫入文字檔案。要將數值資料儲存到文字檔案中,必須先使用函式
str()將其轉換為字串格式。
10.2.2 寫入多行
要讓每個字串都單獨佔一行,需要在write()語句中包含換行符,還可以使用空格、製表符、空行等。
filename = 'programming.txt'
with open(filename, 'w') as file_object:
file_object.write("I love programming.\n")
file_object.write("I love creating new games.\n")
10.2.3 附加到檔案
如果你要給檔案新增內容,而不是覆蓋原有的內容,可以附加模式開啟檔案。如果指定的檔案不存在,python將建立一個空檔案。
#10.2.3 附加到檔案
filename = 'programming.txt'
with open(filename, 'a') as file_object:
file_object.write("I also love finding meaning in large datasets.\n")
file_object.write("I love creating apps that can run in a brower.\n")
10.3 異常
Python使用被稱為異常的特殊物件來管理程式執行期間發生的錯誤。
每當發生讓Python不知所措的錯誤時,它都會建立一個異常物件。如果你編寫了處理該異常的程式碼,程式將繼續執行;
如果你未對異常進行處理,程式將停止,並顯示一個traceback,其中包含有關異常的報告。
異常是使用try-except程式碼塊處理的。
10.3.1 處理ZeroDivisionError異常
10.3.2 使用 try-except 程式碼塊
當你認為可能發生了錯誤時,可編寫一個try-except程式碼塊來處理可能引發的異常。
#10.3.2 使用try-except程式碼塊
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")
如果try程式碼塊中的程式碼執行起來沒有問題,Python將跳過except程式碼塊;如果try程式碼塊中的程式碼導致了錯誤,Python將查詢這樣的except程式碼塊,並執行其中的程式碼,即其中指定的錯誤與引發的錯誤相同。
10.3.3 使用異常避免崩潰
發生錯誤時,如果程式還有工作沒有完成,妥善地處理錯誤就尤其重要。這種情況經常會出現在要求使用者提供輸入的程式中;如果程式能夠妥善地處理無效輸入,就能再提示使用者提供有效輸入,而不至於崩潰。
# 建立一個只執行除法計算的簡單計算器:
print("Give me two numbers,and I'll divide them.")
print("Enter 'q' to quit.")
while True:
first_number = input("\nFirst number: ")
if first_number == 'q':
break
second_number = input("Second number: ")
if second_number == 'q':
break
answer = int(first_number) / int(second_number)
print(answer)
如果5/0的話,程式崩潰可不是好事。
10.3.4 else程式碼塊
只有可能引發異常的程式碼才需要放在try語句中,可提高這個程式抵禦錯誤的能力。依賴於try程式碼塊成功執行的程式碼都應放到else程式碼塊中,如果除法運算成功,我們就使用else程式碼塊來列印結果:
except程式碼塊告訴Python,如果它嘗試執行try程式碼塊中的程式碼時引發了指定的異常,該怎麼辦
#10.3.4 else 程式碼塊
print("Give me two numbers,and I'll divide them.")
print("Enter 'q' to quit.")
while True:
first_number = input("\nFirst number: ")
if first_number == 'q':
break
second_number = input("Second number: ")
try:
answer = int(first_number) / int(second_number)
except ZeroDivisionError:
print("You can't divide by 0! ")
else:
print(answer)
10.3.5 處理FileNotFoundError異常
使用檔案時,一種常見的問題是找不到檔案。
# 嘗試讀取一個不存在的檔案。
filename = 'alice.txt'
with open(filename) as f_obj:
contents = f_obj.read()
在這個示例中,這個錯誤是函式open()導致的,因此要處理這個錯誤,必須將try語句放在包含open()的程式碼行之前:
filename = 'alice.txt'
try:
with open(filename) as f_obj:
contents = f_obj.read()
except FileNotFoundError:
msg = "Sorry, the file " + filename + "does not exist. "
print(msg)
如果檔案不存在,這個程式什麼都不做,因此錯誤處理程式碼的意義不大。
10.3.6 分析文字
你可以分析包含整本書的文字檔案。本節使用的文字來自專案Gutenberg(http://gutenberg.org/)。
提取Alice in Wonderland的文字,並嘗試計算它包含多少個單詞。我們將使用方法split(),它根據一個字串建立一個單詞列表。
**方法split()以空格為分隔符將字串分拆成多個部分,並將這些部分都儲存到一個列表中。**結果是一個包含字串中所有單詞的列表,雖然有些單詞可能包含標點。
#10.3.6 分析文字,用方法split()
else:
#計算檔案大致包含多少個單詞
words = contents.split()
num_words = len(words)
print("The file " + filename + "has about " + str(num_words) + "wods. ")
10.3.7 使用多個檔案
不指定檔案地址,但是也不會報traceback:
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) + "wods. ")
filenames = ['alice.txt','siddhhartha.txt','moby_dik.txt','little.txt']
for filename in filenames:
count_words(filename)
10.3.8 失敗時一聲不吭
要讓程式在失敗時一聲不吭,可像通常那樣編寫try程式碼塊,但在except程式碼塊中明確地告訴Python什麼都不要做。Python有一個pass語句,可在程式碼塊中使用它來讓Python什麼都不要做要讓程式在失敗時一聲不吭,可像通常那樣編寫try程式碼塊,但在except程式碼塊中明確地告訴Python什麼都不要做。Python有一個pass語句,可在程式碼塊中使用它來讓Python什麼都不要做。
pass語句還充當了佔位符,它提醒你在程式的某個地方什麼都沒有做,並且以後也許要在這裡做些什麼。
10.4 儲存資料
使用者輸入某種資訊,程式都把使用者提供的資訊儲存在列表和字典等資料結構中。使用者關閉程式時,你幾乎總是要儲存他們提供的資訊;一種簡單的方式是使用模組json來儲存資料。
模組json讓你能夠將簡單的Python資料結構轉儲到檔案中,並在程式再次執行時載入該檔案的資料。你還可以使用json在Python程式之間分享資料。
10.4.1 使用json.dump()和json.load()
第一個程式將使用json.dump()來儲存這組數字:
函式json.dump()接受兩個實參:要儲存的資料以及可用於儲存資料的檔案物件。
import json
numbers = [2,3,5,7,11,13]
filename = 'number.json'
with open(filename,'w') as f_obj:
json.dump(numbers,f_obj)
第二個程式: 而第二個程式將使用json.load()。
這次我們以讀取方式開啟這個檔案,因為Python只需讀取這個檔案。我們使用函式json.load()載入儲存在number.json中的資訊,
並將其儲存到變數number中。
import json
filename = 'number.json'
with open(filename) as f_obj:
numbers = json.load(f_obj)
print(numbers)
這是一種在程式之間共享資料的簡單方式。
10.4.2 儲存和讀取使用者生成的資料
對於使用者生成的資料,使用json儲存它們大有裨益。
1.先來儲存使用者的名字:
import json
username = input("What is your name? ")
filename = 'username.json'
with open(filename,'w') as f_obj:
json.dump(username,f_obj)
print("We'll remember you when you come back, " + username + "!")
2 . 現在編寫程式,向其名字被儲存的使用者發出問候:
import json
filename = 'username.json'
with open(filename) as f_obj:
username = json.load(f_obj)
print("Welcome back, " + username + "!")
我們使用json.load()將儲存在username.json中的資訊讀取到變數username中。
3. 我們需要將這兩個程式合併到一個程式。
#嘗試寫一個恢復使用者名稱的try程式碼塊,如果這個檔案不存在,,,
import json
# 如果以前儲存了使用者名稱,就載入它
#否則,就提示使用者輸入使用者名稱並存儲它
filename = 'username.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
username = input("What is your name?")
with open(filename,'w') as f_obj:
json.dump(username,f_obj)
print("We'll remember you when you come back, " + username + "!")
else:
print("Welcome back, " + username + "!")
10.4.3 重構
程式碼能夠正確地執行,但可做進一步的改進——將程式碼劃分為一系列完成具體工作的函式。這樣的過程被稱為重構。
import json
def get_stored_username():
"""如果儲存了使用者名稱,就獲取它"""
filename = 'username.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
return None
else:
return username
def get_new_username():
"""提示使用者輸入使用者名稱"""
username = input("What is your name?")
filename = 'username.json'
with open(filename,'w') as f_obj:
json.dump(username,f_obj)
return username
def greet_user():
"""問候使用者,,並指出其名字"""
username = get_stored_username()
if username:
print("Welome back," + username + "!")
else:
username = get_new_username()
print("We'll remember you when you come back, " + username + "!")
greet_user()