2020年10月9日星期五

007 零基礎學的Python 第一篇 Python語言基礎 第7章 使用Python處理檔

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



7章 使用Python處理檔

資料的存儲可以使用資料庫,也可以使用檔。資料庫保持了資料的完整性和關聯性,而且使資料更安全、可靠。使用檔存儲資料則非常簡單、易用,不必安裝資料庫管理系統等運行環境。檔通常用於存儲應用軟體的參數或臨時性資料。Python的檔操作和Java的檔操作十分相似,Python提供了osos.path等模組處理檔。

本章的知識點:

·檔的創建、讀寫和修改

·檔的複製、刪除和重命名

·檔內容的搜索和替換

·文件的比較

·設定檔的讀寫

·目錄的創建和遍歷

·文件和流

 

7.1 檔的常見操作

檔通常用於存儲資料或應用系統的參數。Python提供了osos.pathshutil等模組處理檔,其中包括打開檔、讀寫檔、複製和刪除檔等函數。

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()返回1file物件,file物件可以對檔進行各種操作。

7-1 檔的打開模式


注意  對於圖片、視頻等檔必須使用“b”的模式讀寫。

file類用於檔管理,可以對檔進行創建、打開、讀寫、關閉等操作。file類的常用屬性和方法如表7-2所示。

7-2 file類的常用屬性和方法


檔的處理一般分為以下3個步驟:

1)創建並打開檔,使用file()函數返回1file物件。

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行代碼調用物件fclose()方法,釋放物件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個列表lili中存儲了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.txthello2.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行代碼判斷檔的尾碼名是否為htmlpos+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行代碼輸出結果:


查找到3hello


下麵這段代碼把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()前,需要先生成1SequenceMatcher物件。SequenceMatcher()的聲明如下所示。


class SequenceMatcher( [isjunk[, a[, b]]])


其中,isjunk表示比較過程中是否匹配指定的字元或字串,ab表示待比較的兩個序列。生成序列比較物件後,調用該物件的get_opcodes()方法,將返回1個元組(tag,i1,i2,j1,j2)。tag表示序列分片的比較結果。i1i2表示序列a的索引,j1j2表示序列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.txthi.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.txthi.txt的比較結果。

·第1112行代碼輸出比較結果。如果hello.txthi.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 設定檔的訪問

在應用程式中通常使用設定檔定義一些參數。例如,資料庫設定檔用於記錄資料庫的字元串連接、主機名稱、用戶名、密碼等資訊。Windowsini檔就是一種傳統的設定檔,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行代碼創建1ConfigParser物件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 目錄的常見操作

Pythonos模組和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種基本的流物件——stdinstdoutstderr。這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

日誌檔通常用於記錄應用程式每次操作的執行結果。日誌檔中的記錄便於維護人員瞭解當前系統的狀況,甚至可用於資料的恢復。例如,資料庫日誌檔。Pythonstderr物件用於記錄、輸出異常資訊,通過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.logrecord.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的輸入、輸出流

Javajava.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函數的風格設計,類比JavaFileInputStream類的工作方式。

·第4行代碼以唯讀的方式打開hello.txt

·第5行代碼讀取檔的每行資料。

·第6行代碼讀取每個位元組資料。

·第7行代碼使用yield關鍵字,返回每個位元組資料。

·第13行代碼定義了函數FileOutputStream()。該函數類比JavaFileOutputStream類的工作方式,讀取輸入流的資料並寫入檔hello2.txt

·第15行代碼以寫入的方式打開hello2.txt

·第17行代碼獲取FileInputStream()yield關鍵字產生的位元組。

·第18行代碼把每個位元組寫入hello2.txt中。

注意  檔處理中可能遇到各種非正常的情況,使用tryexcept語法捕獲異常,可以提高程式的健壯性。

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 留言:

發佈留言