2018年10月25日
10.1 從文件中讀取數(shù)據(jù)
要使用文本文件中的信息,首先需要將信息讀取到內(nèi)存中谁帕。為此可以一次性讀取文件的全部內(nèi)容,也可以以每次一行的方式逐步讀取。
10.1.1 讀取整個(gè)文件
下面的程序打開并讀取存有三行圓周率值的文件淋纲,并打印其內(nèi)容:
with open('pi_digits.txt') as file_object:
contents = file_object.read()
print(contents)
要以任何方式使用文件,都得先打開文件院究,這樣才能訪問它洽瞬。在這里,open('pi_digits.txt')
返回一個(gè)表示文件pi_digits.txt
的對象业汰;Python將這個(gè)對象存儲(chǔ)在我們將在后面使用的變量中片任。
關(guān)鍵字with
在不再需要訪問文件后將其關(guān)閉。在這個(gè)程序中蔬胯,注意到我們調(diào)用了open()
对供,但沒有調(diào)用close()
。如果在程序中過早地調(diào)用close()
氛濒,你會(huì)發(fā)現(xiàn)需要使用文件時(shí)它已關(guān)閉产场,這會(huì)導(dǎo)致更多的錯(cuò)誤。并非在任何情況下都能輕松確定變比文件的恰當(dāng)時(shí)機(jī)舞竿,但通過使用前面所示的結(jié)構(gòu)京景,可讓Python去確定:你只管打開文件,并在需要時(shí)使用它骗奖,Python自會(huì)在合適的時(shí)候自動(dòng)將其關(guān)閉确徙。
10.1.2 逐行讀取
讀取文件時(shí)醒串,常常需要檢查其中的每一行,要以每次一行的方式檢查文件鄙皇,可對文件對象使用for循環(huán):
filename = 'pi_digits.txt'
with open(filename) as file_object:
for line in file_object:
print(line)
10.1.3 創(chuàng)建一個(gè)包含文件各行內(nèi)容的列表
使用關(guān)鍵字with
時(shí)芜赌,open()
返回的文件對象只在with
代碼塊內(nèi)可用。如果要在with
代碼塊外訪問文件內(nèi)容伴逸,可在with
代碼塊內(nèi)將文件的各行存儲(chǔ)在一個(gè)列表中缠沈,并在with
代碼塊外使用該列表。
filename = 'pi_digits.txt'
with open(filename) as file_object:
lines = file_object.readlines()
for line in lines:
print(line.rstrip())
10.1.4 使用文件的內(nèi)容
讀取文本文件時(shí)错蝴,Python將其中所有的文本都解讀為字符串洲愤,因此處理文件內(nèi)容就是處理字符串(或每行字符串作為元素的列表)。處理字符串的函數(shù)(如去除末尾換行符的rstrip()
顷锰,替換字符串的replace
柬赐,檢查特定字符串是否出現(xiàn)的in
運(yùn)算符等)都可以在處理文本文件時(shí)使用。
10.2 寫入文件
要將文本寫入文件官紫,在調(diào)用open()
時(shí)需要提供另一個(gè)形參肛宋,告訴Python你要寫入打開的文件。
filename = 'pi_digits.txt'
with open(filename, 'w') as file_object:
file_object.write("I love programming.")
調(diào)用open()
提供的第二個(gè)實(shí)參('w'
)表示以寫入模式打開這個(gè)文件万矾,另外可指定的有讀取模式('r'
)悼吱,附加模式('a'
)或者能夠讀取和寫入文件的模式('r+'
)。忽略模式實(shí)參時(shí)良狈,Python將以默認(rèn)的只讀模式打開文件后添。
如果要寫入的文件不存在,函數(shù)open()
將自動(dòng)創(chuàng)建它薪丁。然而遇西,以寫入('w'
)模式打開文件時(shí)千萬要小心,因?yàn)槿绻付ǖ奈募呀?jīng)存在严嗜,Python將在返回文件對象前清空該文件粱檀。
函數(shù)write()
不會(huì)在你寫入的文本文本末尾添加換行符,因此要寫入多行時(shí)需要在write()
語句中包含換行符漫玄。
如果要給文件添加內(nèi)容茄蚯,而不是覆蓋原有的內(nèi)容,可以附加模式打開文件睦优。以附加模式打開文件時(shí)渗常,Python不會(huì)再返回文件對象前清空文件,而寫入到文件的行都將添加到文件末尾汗盘。如果指定文件不存在皱碘,Python將為你創(chuàng)建一個(gè)空文件。
filename = 'pi_digits.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 browser.\n")
10.3 異常
10.3.1 使用try-except代碼塊
Python使用被稱為異常的特殊對象來管理程序執(zhí)行期間發(fā)生的錯(cuò)誤隐孽。每當(dāng)發(fā)生讓Python不知所措的錯(cuò)誤時(shí)癌椿,它都會(huì)創(chuàng)建一個(gè)異常對象健蕊。如果你編寫了處理該異常的代碼,程序?qū)⒗^續(xù)運(yùn)行踢俄;如果你未對異常進(jìn)行處理缩功,程序?qū)⑼V梗@示一個(gè)traceback褪贵,其中包含有關(guān)異常的報(bào)告掂之。
程序崩潰可不好抗俄,但讓用戶看到traceback也不是好主意脆丁。不懂技術(shù)的用戶會(huì)被它們搞糊涂,而且如果用戶懷有惡意动雹,他會(huì)通過traceback獲悉你不希望他知道的信息槽卫。訓(xùn)練有素的攻擊者可根據(jù)這些信息判斷出可對你的代碼發(fā)起什么樣的攻擊。
異常是使用try-except-else-finally
代碼塊處理的胰蝠,其中else
和finally
代碼塊是可選的歼培。使用了try-except
代碼塊時(shí),即便出現(xiàn)異常茸塞,程序也將繼續(xù)運(yùn)行:顯示你編寫的友好的錯(cuò)誤信息躲庄,而不是令用戶迷惑的traceback。
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")
上述代碼中钾虐,我們將導(dǎo)致錯(cuò)誤的代碼行print(5/0)
放在了一個(gè)try
代碼塊中噪窘,ZeroDivisionError
是一個(gè)異常對象;如果try
代碼塊中的代碼導(dǎo)致了錯(cuò)誤效扫,Python將查找這樣的except
代碼塊倔监,并運(yùn)行其中的代碼,即其中指定的錯(cuò)誤與引發(fā)的錯(cuò)誤相同菌仁。
發(fā)生錯(cuò)誤時(shí)浩习,如果程序還有工作沒有完成,妥善地處理錯(cuò)誤就尤其重要济丘。這種情況經(jīng)常會(huì)出現(xiàn)在要求用戶提供輸入的程序中谱秽;如果程序能夠妥善地處理無效輸入,就能再提示用戶提供有效輸入摹迷,而不至于崩潰疟赊。
下面來創(chuàng)建一個(gè)只執(zhí)行除法運(yùn)算的簡單計(jì)算器:
print("Give me two numbers, and I'll divide them.")
while True:
first_number = input("\nFirst number: ")
second_number = input("Second number: ")
try:
answer = int(first_number) / int(second_number)
print(answer)
except ZeroDivisionError:
print("You can't divide by 0!")
except ValueError:
print("Input are not numbers!")
else:
break;
try-except-else-finally
代碼塊的工作原理大致如下:Python嘗試執(zhí)行try代碼塊中的代碼;只有可能引發(fā)異常的代碼才需要放在try
語句中泪掀。有時(shí)候听绳,有一些僅在try
代碼塊成功執(zhí)行時(shí)才需要運(yùn)行的代碼;這些代碼應(yīng)放在·else
代碼塊中异赫。except代碼塊告訴Python椅挣,如果它嘗試運(yùn)行try
代碼塊中的代碼時(shí)引發(fā)了指定的異常头岔,該怎么辦。
這里需要注意的是鼠证,try
塊中可能引發(fā)錯(cuò)誤的是第一行除法運(yùn)算的代碼峡竣,如果這一行代碼中引發(fā)異常,那么程序會(huì)直接跳轉(zhuǎn)到處理相應(yīng)異常的except
部分量九,后續(xù)的print
語句不會(huì)執(zhí)行适掰,并且循環(huán)會(huì)繼續(xù)下去,只有當(dāng)除法運(yùn)算成功執(zhí)行時(shí)荠列,才會(huì)運(yùn)行后續(xù)的輸出結(jié)果與else
代碼塊中的跳出循環(huán)部分类浪。
10.3.2 決定處理異常的方式
并非每次捕獲到異常時(shí)都需要告訴用戶,也可以在發(fā)生異常時(shí)一聲不吭肌似,就像什么都沒有發(fā)生一樣繼續(xù)運(yùn)行费就。要讓程序在失敗時(shí)一聲不吭,可像通常那樣編寫try
代碼塊川队,但在except
代碼塊中使用pass
語句來明確告訴Python什么都不要做力细。pass
語句還充當(dāng)了占位符,它提醒在程序的某個(gè)地方什么都沒有做固额,并且以后也許要在這里做些什么眠蚂。
應(yīng)該根據(jù)實(shí)際情況來選擇向用戶報(bào)告錯(cuò)誤或者在失敗時(shí)一聲不吭,Python的錯(cuò)誤處理結(jié)構(gòu)讓能夠細(xì)致地控制與用戶分享錯(cuò)誤信息的程度斗躏。
編寫得很好并且經(jīng)過詳盡測試的代碼不容易出現(xiàn)內(nèi)部錯(cuò)誤逝慧,如語法或邏輯錯(cuò)誤,但只要程序依賴于外部因素瑟捣,如用戶輸入馋艺、存在指定的文件、有網(wǎng)絡(luò)鏈接迈套,就有可能出現(xiàn)異常捐祠。