申明: 本站飛宇網 https://feiyetopro.blogspot.com/。自網路收集整理之書籍、文章、影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]。謝謝!

第7章 使用Python處理檔
資料的存儲可以使用資料庫,也可以使用檔。資料庫保持了資料的完整性和關聯性,而且使資料更安全、可靠。使用檔存儲資料則非常簡單、易用,不必安裝資料庫管理系統等運行環境。檔通常用於存儲應用軟體的參數或臨時性資料。Python的檔操作和Java的檔操作十分相似,Python提供了os、os.path等模組處理檔。
本章的知識點:
·檔的創建、讀寫和修改
·檔的複製、刪除和重命名
·檔內容的搜索和替換
·文件的比較
·設定檔的讀寫
·目錄的創建和遍歷
·文件和流
7.1 檔的常見操作
檔通常用於存儲資料或應用系統的參數。Python提供了os、os.path、shutil等模組處理檔,其中包括打開檔、讀寫檔、複製和刪除檔等函數。
7.1.1 文件的創建
Python3中移除了全域的file()函數,還保留了open()函數。檔的打開或創建可以使用函數open()。該函數可以指定處理模式,設置打開的檔為唯讀、只寫或可讀寫狀態。open()的聲明如下所示。
open(file, mode='r', buffering=-1,
encoding=None,
errors=None, newline=None, closefd=True, opener=None) -> file object
【代碼說明】
·參數file是被打開的檔案名稱。如果檔file不存在,open()將創建名為name的文件,然後再打開該文件。
·參數mode是指檔的打開模式。檔的打開模式如表7-1所示。
·參數buffering設置緩存模式。0表示不緩存;1表示行緩衝;如果大於1則表示緩衝區的大小,以位元組為單位。
·open()返回1個file物件,file物件可以對檔進行各種操作。
表7-1 檔的打開模式

注意 對於圖片、視頻等檔必須使用“b”的模式讀寫。
file類用於檔管理,可以對檔進行創建、打開、讀寫、關閉等操作。file類的常用屬性和方法如表7-2所示。
表7-2 file類的常用屬性和方法

檔的處理一般分為以下3個步驟:
1)創建並打開檔,使用file()函數返回1個file物件。
2)調用file物件的read()、write()等方法處理檔。
3)調用close()關閉檔,釋放file物件佔用的資源。
注意 close()方法是必要的。雖然Python提供了垃圾回收機制,清理不再使用的物件,但是手動釋放不再需要的資源是一種良好的習慣。同時也顯式地告訴Python的垃圾回收器:該物件需要被清除。
下麵這段代碼演示了檔的創建、寫入和關閉操作。
01
# 創建文件
02
context = '''hello world'''
03
f = open('hello.txt', 'w')
# 打開文件
04
f.write(context)
# 把字串寫入檔
05
f.close() # 關閉文件
【代碼說明】
·第3行代碼調用open()創建檔hello.txt,設置檔的訪問模式為“w”。open()返回檔物件f。
·第4行代碼把變數context的值寫入檔hello.txt。
·第5行代碼調用物件f的close()方法,釋放物件f佔用的資源。
後面講解的檔讀寫、刪除和複製等操作也將遵循這3個步驟。
7.1.2 文件的讀取
檔的讀取有多種方法,可以使用readline()、readlines()或read()函數讀取檔。下面將一一介紹這些函數讀取檔的實現方法。
1.按行讀取方式readline()
readline()每次讀取檔中的一行,需要使用永真運算式迴圈讀取檔。但當檔指標移動到檔的末尾時,依然使用readline()讀取檔將出現錯誤。因此程式中需要添加1個判斷語句,判斷檔指標是否移動到檔的尾部,並且通過該語句中斷迴圈。下面這段代碼演示了readline()的使用。
01
# 使用readline()讀檔
02
f = open("hello.txt")
03
while True:
04 line = f.readline()
05 if line:
06 print (line)
07 else:
08 break
09
f.close()
【代碼說明】
·第3行代碼使用了“Ture”作為迴圈條件,構成1個永真迴圈。
·第4行代碼調用readline(),讀取hello.txt文件的每一行。每次迴圈依次輸出如下結果。
hello world
hello China
·第5行代碼,判斷變數line是否為真。如果為真,則輸出當前行的內容;否則,退出迴圈。
如果把第4行代碼改為如下語句,讀取的方式略有不同,但讀取的內容完全相同。
line = f.readline(2)
該行代碼並不表示每行唯讀取2個位元組的內容,而是指每行每次讀2個位元組,直到行的末尾。
2.多行讀取方式readlines()
使用readlines()讀取檔,需要通過迴圈訪問readlines()返回清單中的元素。函數readlines()可一次性讀取檔中多行資料。下面這段代碼演示了readlines()讀取檔的方法。
01
# 使用readlines()讀檔
02
f = file('hello.txt')
03
lines = f.readlines()
04
for line in lines: # 一次讀取多行內容
05 print (line)
06
f.close()
【代碼說明】
·第3行代碼調用readlines(),把檔hello.txt中所有的內容存儲在清單lines中。
·第4行代碼迴圈讀取清單lines中的內容。
·第5行代碼輸出清單lines每個元素的內容,即檔hello.txt每行的內容。
·第6行代碼手動關閉文件。
3.一次性讀取方式read()
讀取檔最簡單的方法是使用read(),read()將從檔中一次性讀出所有內容,並賦值給1個字串變數。下面這段代碼演示了read()讀取檔的方法。
01
# 使用read()讀檔
02
f = open("hello.txt")
03
context = f.read()
04
print (context)
05
f.close()
【代碼說明】
·第3行代碼調用read(),把檔hello.txt中所有的內容存儲在變數context中。
·第4行代碼輸出所有檔內容。
可以通過控制read()參數的值,返回指定位元組的內容。
01
f = open("hello.txt")
02
context = f.read(5) # 讀取檔前5個位元組內容
03
print (context)
04
print (f.tell()) # 返回檔物件當前指標位置
05
context = f.read(5) # 繼續讀取5個位元組內容
06 print (context)
07
print (f.tell()) # 輸出檔當前指針位置
08
f.close()
【代碼說明】
·第2行代碼調用read(5),讀取hello.txt檔中前5個位元組的內容,並存儲到變數context中。此時檔的指標移到第5個位元組處。
·第3行代碼輸出變數context的結果,輸出的結果為“hello”。
·第4行代碼調用tell(),輸出當前檔指標的位置:5。
·第5行代碼再次調用read(5),讀取第6個位元組到第10個位元組的內容。
·第6行代碼輸出結果為“worl”。
·第7行代碼輸出當前檔指針的位置:10。
注意 file物件內部將記錄檔指標的位置,以便下次操作。只要file物件沒有執行close()方法,檔指標就不會釋放。
7.1.3 文件的寫入
檔寫入的實現同樣具有多種方法,可以使用write()、writelines()方法寫入檔。7.1.1小節使用了write()方法把字串寫入檔,writelines()方法可以把清單中存儲的內容寫入檔。下面這段代碼演示了如何將清單中的元素寫入檔。
01
# 使用writelines()寫檔
02
f = file("hello.txt", "w+")
03
li = ["hello world\n", "hello China\n"]
04
f.writelines(li)
05
f.close()
【代碼說明】
·第2行代碼使用“w+”的模式創建並打開檔hello.txt。
·第3行代碼定義了1個列表li。li中存儲了2個元素,每個元素代表檔中的1行,“\n”用於換行。
·第4行代碼調用writelines(),把清單li的內容寫入檔中。檔中的內容如下所示。
hello world
hello China
上述兩個方法在寫入前會清除檔中原有的內容,再重新寫入新的內容,相當於“覆蓋”的方式。如果需要保留檔中原有的內容,只是追加新的內容,可以使用模式“a+”打開檔。下麵這段代碼演示了檔的追加操作。
01
# 追加新的內容到檔
02
f = file("hello.txt", "a+") # 寫入方式為追加a+
03
new_context = "goodbye"
04
f.write(new_context)
05
f.close()
【代碼說明】
·第2行代碼使用模式“a+”打開檔hello.txt。
·第4行代碼調用write()方法,hello.txt檔的原有內容保持不變,把變數new_context的內容寫入hello.txt文件的末尾。此時hello.txt中的內容如下所示。
hello world
hello China
goodbye
使用writelines()寫檔的速度更快。如果需要寫入檔的字串非常多,可以使用writelines()提高效率。如果只需要寫入少量的字串,直接使用write()即可。
7.1.4 檔的刪除
檔的刪除需要使用os模組和os.path模組。os模組提供了對系統環境、檔、目錄等作業系統級的介面函數。表7-3列出了os模組常用的檔處理函數。
表7-3 os模組常用的檔處理函數

注意 os模組的open()函數與內建的open()函數的用法不相同。
檔的刪除需要調用remove()函數實現。要刪除檔之前需要先判斷檔是否存在,若存在則刪除檔,否則不進行任何操作。表7-4列出了os.path模組常用的函數。
表7-4 os.path模組常用的函數

下麵這段代碼演示了檔的刪除操作。
01
import os
02
03
file("hello.txt", "w")
04
if os.path.exists("hello.txt"):
05 os.remove("hello.txt")
【代碼說明】
·第3行代碼創建檔hello.txt。
·第4行代碼調用os.path模組的exists()判斷檔hello.txt是否存在。
·第5行代碼調用remove()刪除檔hello.txt。
7.1.5 檔的複製
file類並沒有提供直接複製檔的方法,但是可以使用read()、write()方法,同樣可以實現複製檔的功能。下麵這段代碼把hello.txt的內容複製給hello2.txt。
01
# 使用read()、write()實現複製
02
# 創建文件hello.txt
03
src = file("hello.txt", "w")
04
li = ["hello world\n", "hello China\n"]
05
src.writelines(li)
06
src.close()
07
# 把hello.txt複製到hello2.txt
08
src = open("hello.txt", "r")
09
dst = open("hello2.txt", "w")
10
dst.write(src.read())
11
src.close()
12
dst.close()
【代碼說明】
·第8行代碼以唯讀的方式打開檔hello.txt。
·第9行代碼以只寫的方式打開檔hello2.txt。
·第10行代碼,通過read()讀取hello.txt的內容,然後把這些內容寫入hello2.txt。
shutil模組是另一個檔、目錄的管理介面,提供了一些用於複製檔、目錄的函數。copyfile()函數可以實現檔的複製,copyfile()函數的聲明如下所示。
copyfile(src, dst)
【代碼說明】
·參數src表示原始檔案的路徑,src是字串類型。
·參數dst表示目的檔案的路徑,dst是字串類型。
·該函數把src指向的檔複製到dst指向的檔。
檔的剪切可以使用move()函數實現,該函數的聲明如下所示。
copyfile(src, dst, *,
follow_symlinks=True)
move()的參數和copyfile()相同,移動一個檔或目錄到指定的位置,並且可以根據參數dst重命名移動後的檔。下面這段代碼使用shutil模組實現檔的複製。
01
# shutil模組實現檔的複製
02
import shutil
03
04
shutil.copyfile("hello.txt","hello2.txt")
05
shutil.move("hello.txt","../")
06
shutil.move("hello2.txt","hello3.txt")
【代碼說明】
·第4行代碼調用copyfile(),把hello.txt的內容複製給hello2.txt。
·第5行代碼調用move(),把hello.txt複製到目前的目錄的父目錄,然後刪除hello.txt。相當於把文件hello.txt剪切下來再粘貼到父目錄。
·第6行代碼調用move(),把hello2.txt移動到目前的目錄,並命名為hello3.txt。hello2.txt將被刪除。
7.1.6 檔的重命名
os模組的函數rename()可以對檔或目錄進行重命名。下麵這段代碼演示了檔重命名的操作。如果目前的目錄存在名為hello.txt的檔,則重命名為hi.txt;如果存在hi.txt的檔,則重命名為hello.txt。
01
# 修改檔案名
02
import os
03
li = os.listdir(".")
04
print (li)
05
if "hello.txt" in li:
06 os.rename("hello.txt",
"hi.txt")
07
elif "hi.txt" in li:
08 os.rename("hi.txt",
"hello.txt")
【代碼說明】
·第3行代碼調用listdir()返回目前的目錄的檔列表,其中“.”表示目前的目錄。
·第4行代碼輸出目前的目錄包含的檔,rename_file.py是本段程式的檔。輸出結果如下所示。
['rename_file.py', 'hello.txt']
·第5行代碼判斷目前的目錄中是否存在檔hello.txt。如果存在,則把hello.txt重命名為hi.txt。
·第7行代碼判斷目前的目錄中是否存在檔hi.txt。如果存在,則把hi.txt重命名為hello.txt。
在實際應用中,通常需要把某一類檔修改為另一種類型,即修改檔的尾碼名。這種需求可以通過函數rename()和字串查找的函數實現。下面這段代碼把尾碼名為html的檔修改為以htm為尾碼名的檔。
01
# 修改尾碼名
02
import os
03
files = os.listdir(".")
04
for filename in files:
05 pos = filename.find(".")
06 if filename[pos + 1:] ==
"html":
07 newname = filename[:pos + 1] +
"htm"
08 os.rename(filename,newname)
【代碼說明】
·第3行代碼調用listdir(),返回目前的目錄的檔列表files。這樣可以對目前的目錄中的多個檔進行重命名。
·第4行代碼迴圈列表files,獲取目前的目錄中的每個檔案名。
·第5行代碼調用find()查找檔案名中“.”所在的位置,並把值賦給變數pos。
·第6行代碼判斷檔的尾碼名是否為html。pos+1表示“.”後的位置。
·第7行代碼重新組合新的檔案名,以htm作為尾碼名。filename[:pos+1]表示從filename的開頭位置到“.”這段分片。
·第8行代碼調用rename重命名檔。
為獲取檔的尾碼名,這裡先查找“.”所在的位置,然後通過分片filename[pos+1:]截取尾碼名。這個過程可以使用os.path模組的函數splitext()實現。splitext()返回1個列表,清單中的第1個元素表示檔案名,第2個元素表示檔的尾碼名。修改後的代碼如下所示。
01
import os
02
files = os.listdir(".")
03
for filename in files:
04 li = os.path.splitext(filename)
05 if li[1] == ".html":
06 newname = li[0] +
".htm"
07 os.rename(filename,newname)
【代碼說明】
·第4行代碼調用splitext()對檔案名進行解析,返回檔案名和尾碼名組成的列表。
·第5行代碼,通過li[1]判斷檔是否以html結尾。
·第6行代碼重新組合新的檔案名,li[0]表示不帶尾碼的檔案名。
glob模組用於路徑的匹配,返回符合給定匹配條件的檔列表。glob模組的主要函數就是glob(),該函數返回符合同一匹配條件的多個檔。上面的常式需要判斷檔是否為html尾碼,也可以使用glob()直接匹配檔案名稱。
glob.glob("*.html")
【代碼說明】 該行代碼返回html格式的檔。輸出結果如下所示。
['hello.html', 'hi.html']
glob可以對路徑做更多的匹配。例如,下面這段代碼可以匹配C盤中以w開頭的目錄中所有的文字檔。
glob.glob("c:\\w*\\*.txt")
注意 glob()的參數滿足規則運算式的語法。
7.1.7 檔內容的搜索和替換
檔內容的搜索和替換可以結合前面學習的字串查找和替換來實現。例如,從hello.txt檔中查找字串“hello”,並統計“hello”出現的次數。hello.txt文件如下所示。
1 hello world
3 hello hello China
下麵這段代碼從hello.txt中統計字串“hello”的個數。
01
# 文件的查找
02
import re
03
04
f1 = file("hello.txt", "r")
05
count = 0
06
for s in f1.readlines():
07 li = re.findall("hello", s)
08 if len(li) > 0:
09
count = count + li.count("hello")
10
print ("查找到" + str(count) + "個hello")
11
f1.close()
【代碼說明】
·第5行代碼定義變數count,用於計算字串“hello”出現的次數。
·第6行代碼每次從檔hello.txt中讀取1行到變數s。
·第7行代碼調用re模組的函數findall()查詢變數s,把查找的結果存儲到清單li中。
·第8行代碼,如果清單中的元素個數大於0,則表示查找到字串“hello”。
·第9行代碼調用列表的count()方法,統計當前列表中“hello”出現的次數。
·第10行代碼輸出結果:
查找到3個hello
下麵這段代碼把hello.txt中的字串“hello”全部替換為“hi”,並把結果保持到檔hello2.txt中。
01
# 文件的替換
02
f1 = file("hello.txt", "r")
03
f2 = file("hello2.txt", "w")
04
for s in f1.readlines():
05 f2.write(s.replace("hello",
"hi"))
06
f1.close()
07
f2.close()
【代碼說明】
·第4行代碼從hello.txt中讀取每行內容到變數s中。
·第5行代碼先使用replace()把變數s中的“hello”替換為“hi”,然後把結果寫入檔hello2.txt中。文件hello2.txt的內容如下所示。
1 hi world
3 hi hi China
7.1.8 文件的比較
Python提供了模組difflib,現實對序列、檔的比較。如果要比較兩個檔,列出兩個檔的異同,可以使用difflib模組的SequenceMatcher類實現。其中的方法get_opcodes()可以返回兩個序列的比較結果。調用方法get_opcodes()前,需要先生成1個SequenceMatcher物件。SequenceMatcher()的聲明如下所示。
class SequenceMatcher( [isjunk[, a[,
b]]])
其中,isjunk表示比較過程中是否匹配指定的字元或字串,a、b表示待比較的兩個序列。生成序列比較物件後,調用該物件的get_opcodes()方法,將返回1個元組(tag,i1,i2,j1,j2)。tag表示序列分片的比較結果。i1、i2表示序列a的索引,j1、j2表示序列b的索引。表7-5列出了get_opcodes()返回元組(tag,i1,i2,j1,j2)的含義。
表7-5 get_opcodes()返回元組(tag,i1,i2,j1,j2)的含義

注意 SequenceMatcher(None,a,b)創建序列比較對象,將以a作為參考標準進行比較;SequenceMatcher(None,b,a)創建序列比較對象,則以b作為參考標準進行比較。
例如,有兩個文字檔hello.txt與hi.txt,現要獲得兩個文件的異同。hello.txt檔的內容如下所示。
hello world
hi.txt檔的內容如下所示。
hi hello
下麵這段代碼以hello.txt檔為參照,實現了兩個檔的比較,並返回比較結果。
01
import difflib
02
03
f1 = file("hello.txt", "r")
04
f2 = file("hi.txt", "r")
05
src = f1.read()
06
dst = f2.read()
07
print (src)
08
print (dst)
09
s = difflib.SequenceMatcher( lambda x: x == "", src, dst)
10
for tag, i1, i2, j1, j2 in s.get_opcodes():
11 print ("%s src[%d:%d]=%s
dst[%d:%d]=%s" % \
12 (tag, i1, i2, src[i1:i2], j1, j2,
dst[j1:j2]))
【代碼說明】
·第9行代碼生成1個序列匹配的物件s,其中lambda x:x==""表示忽略hi.txt中的分行符號。如果hi.txt中有多餘的換行,並不會作為不同點返回。
·第10行代碼調用方法get_opcodes()獲取檔hello.txt與hi.txt的比較結果。
·第11、12行代碼輸出比較結果。如果hello.txt與hi.txt的內容需要完全一致,在hello.txt的開始處插入字串“hi”,並刪除hello.txt中的字串“world”,hello.txt的內容分片[0:5]與hi.txt的內容分片[3:8]相同。比較結果如下所示。
insert src[0:0]= dst[0:3]=hi
equal src[0:5]=hello dst[3:8]=hello
delete src[5:11]= world dst[8:8]=
7.1.9 設定檔的訪問
在應用程式中通常使用設定檔定義一些參數。例如,資料庫設定檔用於記錄資料庫的字元串連接、主機名稱、用戶名、密碼等資訊。Windows的ini檔就是一種傳統的設定檔,ini檔由多個塊組成,每個塊由有多個配置項組成。例如,以下ODBC.ini檔中記錄了Windows環境下各種資料庫存儲系統的ODBC驅動資訊。
[ODBC 32 bit Data Sources]
MS Access Database=Microsoft Access
Driver (*.mdb) (32 λ)
Excel Files=Microsoft Excel Driver (*.xls)
(32 λ)
dBASE Files=Microsoft dBase Driver
(*.dbf) (32 λ)
[MS Access Database]
Driver32=C:\WINDOWS\system32\odbcjt32.dll
[Excel Files]
Driver32=C:\WINDOWS\system32\odbcjt32.dll
[dBASE Files]
Driver32=C:\WINDOWS\system32\odbcjt32.dll
其中每個方括號表示一個配置塊,配置塊下的多個賦值運算式就是配置項。Python標準庫的configparser模組用於解析設定檔。ConfigParser模組的ConfigParser類可讀取ini檔的內容。下麵這段代碼從ODBC.ini檔中讀取每個配置塊、配置項的標題和配置的內容。
01
# 讀設定檔
02
import configparser
03
04
config = configparser.ConfigParser()
05
config.read("ODBC.ini")
06
sections = config.sections() # 返回所有的配置塊
07
print ("配置塊:", sections)
08
o = config.options("ODBC 32 bit Data Sources") # 返回所有的配置項
09
print ("配置項:", o)
10
v = config.items("ODBC 32 bit Data Sources")
11
print ("內容:", v)
12
# 根據配置塊和配置項返回內容
13
access = config.get("ODBC 32 bit Data Sources", "MS
Access Database")
14
print (access)
15
excel = config.get("ODBC 32 bit Data Sources", "Excel
Files")
16
print (excel)
17
dBASE = config.get("ODBC 32 bit Data Sources", "dBASE
Files")
18
print (dBASE)
【代碼說明】
·第4行代碼創建1個ConfigParser物件config。
·第5行代碼讀取ODBC.ini文件。
·第6行代碼調用sections()返回配置塊的標題。
·第7行代碼輸出配置塊的標題。輸出結果如下所示。
配置塊: ['ODBC 32 bit Data Sources', 'Excel Files', 'dBASE
Files', 'MS Access Database']
·第8行代碼調用options()返回“ODBC 32 bit Data Sources”塊下各配置項的標題。
·第9行代碼輸出配置項的標題。輸出結果如下所示。
配置項: ['ms access database', 'dbase files', 'excel files']
·第10行代碼調用items()返回“ODBC 32 bit Data Sources”塊下各配置項的內容。
·第11行代碼輸出配置項的內容。輸出結果如下所示。
內容: [('ms access database', 'Microsoft Access Driver (*.mdb)
(32 \xce\xbb)'), ('dbase files', 'Microsoft dBase Driver (*.dbf) (32
\xce\xbb)'), ('excel files', 'Microsoft Excel Driver (*.xls) (32 \xce\xbb)')]
·第13~18行代碼調用get()方法分別獲取“ODBC
32 bit Data Sources”塊下每個配置項的內容。
設定檔的寫入操作也很簡單。首先調用add_section()方法添加1個新的配置塊,然後調用set()方法,設置配置專案,最後寫入設定檔ODBC.ini即可。
01
# 寫設定檔
02
import configparser
03
config = configparser.ConfigParser()
04
config.add_section("ODBC Driver Count") # 添加新的配置塊
05
config.set("ODBC Driver Count", "count", 2) # 添加新的配置項
06
f = open("ODBC.ini", "a+")
07
config.write(f)
08
f.close()
【代碼說明】
·第4行代碼調用add_section()添加配置塊。
·第5行代碼調用set()添加配置項,並設置其內容。
·第6行代碼以“a+”模式打開檔ODBC.ini,追加新的配置。
·第7行代碼寫設定檔。
設定檔的修改需要先讀取ODBC.ini檔,然後調用set()方法設置指定配置塊下某個配置項的值,最後寫入設定檔ODBC.ini。下麵這段代碼在ODBC.ini檔中修改了配置塊“ODBC Driver Count”。
01
# 修改設定檔
02
import configparser
03
config = configparser.ConfigParser()
04
config.read("ODBC.ini")
05
config.set("ODBC Driver Count", "count", 3) # 修改配置項
06
f = open("ODBC.ini", "r+")
07
config.write(f)
08
f.close()
【代碼說明】
·第4行代碼讀設定檔ODBC.ini。
·第5行代碼調用set()修改“ODBC Driver Count”下的配置項“count”的值為3。
·第6行代碼以“r+”打開設定檔ODBC.ini。
·第7行代碼寫設定檔。
如果要刪除某個配置塊,調用remove_section()方法,傳遞需要刪除的配置塊名作為參數。如果要刪除指定配置塊下的某個配置項,調用remove_option()方法,傳遞需要刪除的配置塊名和配置項名作為參數。下麵這段代碼先刪除ODBC.ini檔中配置項“count”,然後刪除配置塊“ODBC Driver Count”。
01
# 刪除設定檔
02
import configparser
03
config = configparser.ConfigParser()
04
config.read("ODBC.ini")
05
config.remove_option("ODBC Driver Count",
"count") # 刪除配置項
06
config.remove_section("ODBC Driver Count") # 刪除配置塊
07
f = open("ODBC.ini", "w+")
08
config.write(f)
09
f.close()
【代碼說明】
·第5行代碼調用remove_option()刪除“ODBC Driver Count”下的配置項“count”。
·第6行代碼調用remove_section()刪除“ODBC Driver Count”配置塊。
·第7行代碼以“w+”模式寫設定檔。
7.2 目錄的常見操作
Python的os模組和os.path模組同樣提供了一些針對目錄操作的函數。
7.2.1 創建和刪除目錄
os模組同樣提供了一些針對目錄進行操作的函數。表7-6列出了os模組中常用的目錄處理函數。
表7-6 os模組常用的目錄處理函數

目錄的創建和刪除可以使用mkdir()、makedirs()、rmdir()、removedirs()實現。
01
import os
02
03
os.mkdir("hello")
04
os.rmdir("hello")
05
os.makedirs("hello/world")
06
os.removedirs("hello/world")
【代碼說明】
·第3行代碼創建1個名為“hello”的目錄。
·第4行代碼刪除目錄“hello”。
·第5行代碼創建多級目錄,先創建目錄“hello”,再創建子目錄“world”。
·第6行代碼刪除目錄“hello”和“world”。
注意 如果需要一次性創建、刪除多個目錄,應使用函數makedirs()和removedirs()。而mkdir()或rmdir()一次只能創建或刪除一個目錄。
7.2.2 目錄的遍歷
目錄的遍歷有兩種實現方法:遞迴函數、os.walk()。這兩種方法的實現原理和步驟各不相同,下面一一介紹這些方法的使用。
1.遞迴函數
對於目錄的遍歷可以通過遞迴函數實現。5.3.5小節講解了遞迴函數,遞迴函數可以在函數主體內直接或間接地調用函數本身。下面這段代碼對第7章中的原始程式碼目錄進行遍歷,輸出該目錄下所有的檔案名稱。
01
# 遞迴遍歷目錄
02
import os
03
def VisitDir(path):
04 li = os.listdir(path)
05 for p in li:
06 pathname = os.path.join(path, p)
07 if not os.path.isfile(pathname):
08 VisitDir(pathname)
09 else:
10 print (pathname)
11
12
if __name__ == "__main__":
13 path = r"D:\developer\python\example\07"
14 VisitDir(path)
【代碼說明】
·第3行代碼定義了名為VisitDir()的函數,該函數以目錄路徑作為參數。
·第4行代碼返回當前路徑下所有的目錄名和檔案名。
·第6行代碼調用os.path模組的函數join(),獲取檔的完整路徑,並保存到變數pathname。
·第7行代碼判斷pathname是否為檔。如果pathname表示目錄,則遞迴呼叫VisitDir()繼續遍歷底層目錄。否則,直接輸出檔的完整路徑。遍歷輸出結果如下所示。
D:\developer\python\example\07\7.1.1\create_file.py
D:\developer\python\example\07\7.1.1\hello.txt
D:\developer\python\example\07\7.1.2\hello.txt
D:\developer\python\example\07\7.1.2\read_file.py
D:\developer\python\example\07\7.1.3\write_file.py
D:\developer\python\example\07\7.1.3\hello.txt
D:\developer\python\example\07\7.1.4\delete_file.py
D:\developer\python\example\07\7.1.5\copy_file.py
D:\developer\python\example\07\7.1.5\hello3.txt
D:\developer\python\example\07\7.1.6\rename_file.py
D:\developer\python\example\07\7.1.6\alter_extension.py
D:\developer\python\example\07\7.1.6\hi.txt
D:\developer\python\example\07\7.1.6\hello.html
D:\developer\python\example\07\7.1.6\hi.html
D:\developer\python\example\07\7.1.7\find_file.py
D:\developer\python\example\07\7.1.7\hello.txt
D:\developer\python\example\07\7.1.7\hello3.txt
D:\developer\python\example\07\7.1.8\difflib.py
D:\developer\python\example\07\7.1.8\hello.txt
D:\developer\python\example\07\7.1.8\hi.txt
D:\developer\python\example\07\7.1.9\config_file.py
D:\developer\python\example\07\7.1.9\ODBC.INI
D:\developer\python\example\07\7.2.1\make_remove_dir.py
D:\developer\python\example\07\7.2.2\visitdir.py
·第14行代碼調用VisitDir()作為程式的起點,遍歷D:\developer\python\example\07下所有目錄。
2.os.walk()
os模組也提供了同名函數walk(),該函數是Python2.3開始發佈的。os.walk()可用於目錄的遍歷,功能類似於os.path模組的函數walk()。os.walk()不需要回呼函數,更容易使用。os.walk()的函式宣告如下所示。
walk(top, topdown=True, onerror=None,
followlinks=False)
【代碼說明】
·參數top表示需要遍歷的目錄樹的路徑。
·參數topdown預設值為True,表示首先返回目錄樹下的檔,然後再遍歷目錄樹的子目錄。topdown的值為False,則表示先遍歷目錄樹的子目錄,返回子目錄中的檔,最後返回根目錄下的檔。
·參數onerror預設值為None,表示忽略檔遍歷時產生的錯誤。如果不為空,則提供一個自訂函數提示錯誤資訊後繼續遍歷或拋出異常中止遍歷。
·該函數返回1個元組,該元組有3個元素。這3個元素分別表示每次遍歷的路徑名、目錄清單和檔清單。
下面這段代碼使用了os.walk()遍歷目錄D:\developer\python\example\07。
01
import os
02
03
def VisitDir(path):
04 for root,dirs,files in os.walk(path):
05 for filepath in files:
06 print (os.path.join(root,
filepath))
07
08
if __name__=="__main__":
09 path =
r"D:\developer\python\example\07"
10 VisitDir(path)
使用os模組的函數walk()只要提供1個參數path,即待遍歷的目錄樹的路徑。os.walk()實現目錄遍歷的輸出結果和遞迴函數實現目錄遍歷的輸出結果相同。
注意 os.path.walk()在Python3中已經被移除。
7.3 文件和流
讀寫資料的方式有多種,如檔的讀寫、資料庫的讀寫。為了有效地表示資料的讀寫,把檔、外設、網路連接等資料傳輸抽象地表示為流。資料的傳輸就好像流水一樣,從一個容器流到另一個容器。程式中資料的傳輸也是如此。
7.3.1 Python的流對象
Python隱藏了流機制,在Python的模組中找不到類似Stream的類。Python把檔的處理和流關聯在一起,流物件實現了File類的所有方法。sys模組提供了3種基本的流物件——stdin、stdout、stderr。這3個物件分別表示標準輸入、標準輸出和錯誤輸出。流物件可以使用File類的屬性和方法,流物件的處理方式和檔的處理方式相同。
1.stdin
物件stdin表示流的標準輸入。下面這段代碼就是通過流物件stdin讀取檔hello.txt的內容。
01
import sys
02
03
sys.stdin = open("hello.txt", "r")
04
for line in sys.stdin.readlines():
05 print (line)
【代碼說明】 第3行代碼調用open()時,在賦值運算式的左側使用sys.stdin替換檔物件。可見,流物件的使用方法和檔物件相同。
2.stdout
物件stdout表示流的標準輸出。前面的程式都是把程式運行後的結果輸出到控制台,這裡通過stdout對象重定向輸出,把輸出的結果保存到檔中。
01
import sys
02
03
sys.stdout = open(r"./hello.txt", "a")
04
print ("goodbye")
05
sys.stdout.close()
【代碼說明】
·第3行代碼調用open()以追加模式打開目前的目錄下的檔hello.txt,並把hello.txt設置為終端輸出設備。預設的終端輸出設備是系統的控制台。
·第4行代碼向檔hello.txt輸出字串“goodbye”。
·第5行代碼關閉物件sys.stdout。
3.stderr
日誌檔通常用於記錄應用程式每次操作的執行結果。日誌檔中的記錄便於維護人員瞭解當前系統的狀況,甚至可用於資料的恢復。例如,資料庫日誌檔。Python的stderr物件用於記錄、輸出異常資訊,通過stderr物件可以實現日誌檔的功能。例如,目前的目錄中的檔hello.txt內容為空,則在日誌檔error.log中記錄異常資訊。如果檔hello.txt的內容不為空,則在日誌檔error.log中記錄正確的資訊。下面這段代碼實現了這部分功能。
01
import sys,time
02
03
sys.stderr = open("record.log", "a")
04
f = open(r"./hello.txt","r")
05
t = time.strftime("%Y-%m-%d %X", time.localtime())
06
context = f.read()
07
if context:
08 sys.stderr.write(t + " " +
context)
09
else:
10 raise Exception, t + " 異常資訊"
【代碼說明】
·第3行代碼調用open()以追加模式打開目前的目錄下的日誌檔record.log。record.log成為記錄日誌的設備。
·第4行代碼讀取檔hello.txt。
·第5行代碼獲取當前的系統時間。
·第6行代碼讀取檔hello.txt的內容,並賦給變數context。
·第7行代碼判斷檔hello.txt的內容是否為空。
·第8行代碼,如果檔的內容不為空,則向record.log檔記錄當前時間和hello.txt的內容。
2008-02-24 19:30:09 goodbye
·第10行代碼,如果檔的內容為空,則拋出異常,並在record.log檔中記錄當前時間和異常資訊。輸出結果:
Traceback (most recent call last):
File "stderr.py", line 12, in <module>
raise Exception, t + " 異常資訊"
Exception: 2008-02-24 17:56:46 異常資訊
7.3.2 模擬Java的輸入、輸出流
Java的java.io包提供了一系列處理流的類,根據資料流程動的方向分為輸入流和輸出流。例如,InputStream類、OutputStream類。通過第5.3.7節學習的Generator函數,可以很方便地構造出流的實現函數。下面這段代碼模擬Java的輸入、輸出流讀寫文字檔。首先調用函數FileInputStream()讀取檔hello.txt的內容,然後調用函數FileOutputStream()把FileInputStream()讀取的內容寫入檔hello2.txt中。
01 # 檔輸入流
02
def FileInputStream(filename):
03 try:
04 f = open(filename)
05 for line in f:
06 for byte in line:
07 yield byte
08 except StopIteration, e:
09
f.close()
10 return
11
12
# 檔輸出流
13
def FileOutputStream(inputStream, filename):
14 try:
15 f = open(filename, "w")
16 while True:
17 byte = inputStream.next()
18 f.write(byte)
19 except StopIteration, e:
20 f.close()
21 return
22
23
if __name__ == "__main__":
24
FileOutputStream(FileInputStream("hello.txt"), "hello2.txt")
【代碼說明】
·第2行代碼定義了函數FileInputStream()。該函數採用Generator函數的風格設計,類比Java的FileInputStream類的工作方式。
·第4行代碼以唯讀的方式打開hello.txt。
·第5行代碼讀取檔的每行資料。
·第6行代碼讀取每個位元組資料。
·第7行代碼使用yield關鍵字,返回每個位元組資料。
·第13行代碼定義了函數FileOutputStream()。該函數類比Java的FileOutputStream類的工作方式,讀取輸入流的資料並寫入檔hello2.txt。
·第15行代碼以寫入的方式打開hello2.txt。
·第17行代碼獲取FileInputStream()中yield關鍵字產生的位元組。
·第18行代碼把每個位元組寫入hello2.txt中。
注意 檔處理中可能遇到各種非正常的情況,使用try…except語法捕獲異常,可以提高程式的健壯性。
7.4 檔處理示例——檔案屬性流覽程式
本節將運用前面學習的內容,設計一個流覽檔案屬性的程式。通過給定的目錄路徑查看檔的名稱、大小、創建時間、最後修改日期和最後訪問日期。設計一個函數showFileProperties(path),path表示目錄的路徑,該函數可以查看path目錄下所有檔的屬性。showFileProperties()的實現大致分為如下3個步驟。
1)遍歷path指定的目錄,獲取每個子目錄的路徑。
2)遍歷子目錄下的所有檔,並返回檔的屬性清單。
3)分解屬性清單,對屬性清單的值進行格式化輸出。
步驟1可以採用7.2.2小節的思路實現目錄的遍歷。步驟2通過os模組的函數stat()返回檔的屬性清單,該屬性清單包括檔的大小、創建時間、最後修改時間、最後存取時間等資訊。步驟3通過清單的索引獲取檔的各個屬性。對於檔的時間屬性,調用time模組的函數localtime()返回時間類型,並結合字串的格式化輸出檔的時間屬性。下面這段代碼調用函數showFileProperties(),列出了D:\developer\python\example\07\7.2.2路徑下所有檔的屬性。
01
def showFileProperties(path):
02 '''顯示檔的屬性。包括路徑、大小、創建日期、最後修改時間,最後存取時間'''
03 import time,os
04 for root,dirs,files in
os.walk(path,True):
05 print ("位置:" + root)
06 for filename in files:
07 state =
os.stat(os.path.join(root, filename))
08 info = "檔案名: " + filename + " "
09 info = info + "大小:" + ("%d" % state[-4]) + " "
10
t = time.strftime("%Y-%m-%d
%X", time.localtime(state[-1]))
11 info = info + "創建時間:" + t + " "
12 t =
time.strftime("%Y-%m-%d %X", time.localtime(state[-2]))
13 info = info + "最後修改時間:" + t + " "
14 t =
time.strftime("%Y-%m-%d %X", time.localtime(state[-3]))
15 info = info + "最後存取時間:" + t + " "
16 print (info)
17
18
if __name__ ==
"__main__":
19 path = r"D:\developer\python\example\07\7.2.2"
20 showFileProperties(path)
【代碼說明】
·第4行代碼調用函數os.walk()對path指定的路徑進行遍歷。
·第5行代碼輸出path路徑下每個子目錄的路徑。輸出結果如下所示。
位置:D:\developer\python\example\07\7.2.2
·第6行代碼對每個子目錄進行遍歷,返回檔案名稱。
·第7行代碼調用os.stat()返回檔的屬性清單。
·第9行代碼,state[-4]表示檔的大小。
·第10行代碼調用time模組的strftime()把時間類型轉換為字串。其中state[-1]表示檔的創建時間。
·第12行代碼,state[-2]表示檔的最後修改時間。
·第14行代碼,state[-3]表示檔的最後存取時間。
·第16行代碼輸出檔的屬性資訊。輸出的內容如下所示。
檔案名: visitdir.py 大小:392 創建時間:2008-2-22 10:46:49 最後修改時間:2008-2-22 11:9:26 最後存取時間:2008-2-24 0:0:0
檔案名: os_path_walk.py 大小:335 創建時間:2008-2-15 17:1:44 最後修改時間:2008-2-24 12:32:28 最後存取時間:2008-2-24 0:0:0
檔案名: os_walk.py 大小:321 創建時間:2008-2-22 12:45:5 最後修改時間:2008-2-22 14:42:22 最後存取時間:2008-2-24 0:0:0
注意 os.stat()的參數必須是絕對路徑。因此,需要先調用os.path.join(root,filename)連接檔的路徑和檔案名。
7.5 小結
本章介紹了Python檔程式設計方面的知識。講解了檔的打開、讀取、寫入、刪除等操作的實現,以及針對目錄的程式設計,包括目錄的創建、刪除、遍歷等。重點介紹了目錄的遍歷,並採用了遞迴函數以及os.walk()兩種方法實現目錄的遍歷。結合開發中經常使用的設定檔,講解了對設定檔的配置項進行添加、刪除、修改等操作。Python對流機制實現了很好的封裝,程式師對檔的程式設計就是通過流的方式處理的。介紹了Python中常見的流物件,並實現了流的重定向。最後結合目錄的遍歷、時間的處理、字串的格式化等知識點,實現了查看檔案屬性的程式。
下一章將介紹Python物件導向的技術。Python作為一門物件導向的語言,簡化了類的創建和調用,其中許多特性都可以通過符合和約定實現。物件導向的技術將資料和功能封裝起來,合理地組織物件之間的關係,便於程式的維護和擴展。
7.6 習題
文件test.txt中包含以下內容:
今年是2014年。
2014年你好。
2014年再見。
1)讀取該檔,並輸出所有內容。
2)去掉檔內容中的換行。
3)計算出文件的長度。
4)使用歐冠2015替換2014。
5)創建另一個檔test2.txt,寫入本檔的內容。
0 留言:
發佈留言