1. 程式人生 > >《Python程式設計:從入門到實踐》第十章:檔案和異常

《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在不再需要訪問檔案後將其關閉。也可以

呼叫open()和close()來開啟或者關閉檔案;但是在程式存在bug的時候,導致close()語句未執行,檔案將不會關閉。未妥善的關閉檔案會導致資料丟失或者受損。
使用方法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()