Python:讀寫檔案(I/O) | 組織檔案

thoustree 發表於 2021-04-22

1. I/O 概述 

   程式與使用者互動涉及到程式的輸入輸出(I/O)

  一種型別是字串,通過input() print() 函式以及資料型別轉換類函式如(int()),實現資料的輸入輸出。

  另一種型別是讀寫檔案,通過檔案的建立、讀和寫,實現資料的輸入輸出。

  本文敘述關於讀寫檔案與組織檔案 

2. 檔案與檔案路徑

2.1  檔案及檔案型別

  2.1.1 檔案

   檔案是一個的序列,可被應用程式翻譯成文字檔案和二進位制檔案。

  是儲存在計算機中的最小單位,位代表裝置的某一狀態,但只能是兩種狀態之一(裝置開關斷開或合上)。

  檔案可分類為文字檔案和二進位制檔案

  2.1.2 文字檔案

  文字檔案是字元檔案,只含有ASCII或Unicode字元,要儲存整數、浮點數或其他資料結構,必須要將它們轉化成對應的字元格式。

  2.1.3 二進位制檔案

  二進位制檔案是其他所有檔案。諸如字處理文件、 PDF、 影像、 電子表格和可執行程式。

2.2  檔案路徑

   檔案以資料夾 (folder)的形式組織起來。資料夾也叫目錄(directory)。

  每個正在執行的程式都有一個 ‘‘當前目錄”作為大多數操作的預設目錄。例如,當你開啟一個檔案來讀取時,Python 會在當前目錄下尋找這個檔案。

  指明一個檔案或者目錄的字串,叫做路徑 (path),比如:'/Python36/scripts'

  os 模組提供了操作檔案和目錄的函式。

  2.2.1 相對路徑

   一個簡單的檔名,如 memo.txt ,同樣被看做是一個路徑,只不過是相對路徑  ,因為它是相對於當前目錄而言的。相對路徑(relative path)就是從當前目錄開始的路徑。

  下面程式碼揭示了 os.path.exists() 的實際

>>> import os 
>>> os.getcwd()  # getcwd() 返回當前工作目錄
'D:\\Python36'
>>> os.chdir('C:\\Windows\\System32')  # chdir() 切換當前目錄
>>> os.getcwd()
'C:\\Windows\\System32'

 

>>> import os
>>> os. path . exists ('(讀寫檔案)瘋狂填詞2.txtt ') # 這個檔案在計算機上存在,但不存在當前目錄
False
>>> os.path.exists('正則程式碼實踐strip.py') # 這個檔案存在當前目錄
True

  2.2.2 絕對路徑

  一個以 開頭的路徑和當前目錄無關,叫做絕對路徑 絕對路徑 (absolute path)就是從檔案系統頂部開始的路徑。

    下面程式碼揭示了 os.path.abspath() 的實際效果以及 os.path.relpath() 的用法

>>> import os
>>> os.path.abspath('正則程式碼實踐strip.py')
'D:\\Python36\\正則程式碼實踐strip.py'
>>> import os
>>> os.path.abspath('正則程式碼實踐strip.py')
'D:\\Python36\\正則程式碼實踐strip.py'
>>> os.path.abspath('(讀寫檔案)瘋狂填詞2.py') # 這個檔案是當前目錄沒有的
'D:\\Python36\\(讀寫檔案)瘋狂填詞2.py'
>>> os.path.abspath(r'\Python程式設計快速上手++讓繁瑣工作自\(讀寫檔案)瘋狂填詞2.py') # 這個目錄也是當前目錄不存在的
'D:\\Python程式設計快速上手++讓繁瑣工作自\\(讀寫檔案)瘋狂填詞2.py'
>>> os.path.abspath(r'\Desktop\Python程式設計快速上手++讓繁瑣工作自動化\(讀寫檔案)瘋狂填詞2.py')
'D:\\Desktop\\Python程式設計快速上手++讓繁瑣工作自動化\\(讀寫檔案)瘋狂填詞2.py'
>>> os.path.abspath(r'\Administrator\Desktop\Python程式設計快速上手++讓繁瑣工作自動化\(讀寫檔案)瘋狂填詞2.py')
'D:\\Administrator\\Desktop\\Python程式設計快速上手++讓繁瑣工作自動化\\(讀寫檔案)瘋狂填詞2.py' # 如果沒有指明最頂部根目錄,則該函式返回的目錄的頂部都是當前目錄的頂部,即"D:\\"
>>> os.path.abspath(r'C:\Users\Administrator\Desktop\Python程式設計快速上手++讓繁瑣工作自動化\(讀寫檔案)瘋狂填詞2.py')
'C:\\Users\\Administrator\\Desktop\\Python程式設計快速上手++讓繁瑣工作自動化\\(讀寫檔案)瘋狂填詞2.py'
>>> import os
>>> os.path.relpath('C:\\Windows', 'C:\\')
'Windows'
>>> os.path.relpath('C:\\Windows', 'C:\\spam\\eggs')
'..\\..\\Windows'
>>> os.getcwd()
'D:\\Python36'

  2.2.3 用 os.makedirs() 建立新資料夾(目錄)

  os.makedirs()將建立所有必要的中間資料夾,目的是確保完整路徑名存在。

>>> import os
>>> os.makedirs('D:\\abc\\def\\g')
>>> os.chdir(('D:\\abc\\def\\g')

  2.2.4  用 os.path.join() 構建所有作業系統上都有效的路徑

  這個方法很有用,例如構建檔案路徑(建立檔案的同時建立路徑)

>>> import os
>>> os.path.join('D:\\Python36', 'test.txt')
'D:\\Python36\\test.txt'

3. 讀寫檔案

3.1  用 open() 函式建立或開啟檔案

  open() 函式返回一個 File 型別物件,將該物件儲存於變數中,就可以呼叫 File 物件的方法

>>> file = open('hello.txt')

  如果當前目錄下沒有 'hello.txt' 檔案,則建立該檔案並開啟。

3.2  read() 或 readlines()方法讀取檔案

   open() 函式預設以“讀”模式開啟檔案,因此不能進行寫入操作,但可以用 read() 方法讀取檔案內容(但要實現下面程式碼結果,先要手動開啟檔案,並敲入對應的內容),

  read() 方法就返回儲存在該檔案中的字串。

  readlines() 方法,從檔案取得一個字串的列表。列表中的每個字串就是文字中的每一行。

>>> helloContent = file.read()
>>> helloContent
'Hello world!'

 

>>> sonnetFile = open('sonnet29.txt')
>>> sonnetFile.readlines()
[When, in disgrace with fortune and men's eyes,\n', ' I all alone beweep my
outcast state,\n', And trouble deaf heaven with my bootless cries,\n', And
look upon myself and curse my fate,']

3.3  write()方法寫入檔案

  'w'作為第二個引數傳遞給 open(),將以寫模式開啟該檔案,便可以呼叫 write() 方法將內容寫入檔案。

  'w' 模式將會刪除檔案原有內容,重新寫入。

  如果不希望刪除原有內容,可以用 'a' 模式將內容以新增的方式寫入檔案。

  還有二進位制寫入模式 'wb' 等,更多模式請參考Python文件。

>>> baconFile = open('bacon.txt', 'w')
>>> baconFile.write('Hello world!\n')
13
>>> baconFile.close()
>>> baconFile = open('bacon.txt', 'a')
>>> baconFile.write('Bacon is not a vegetable.')
25
>>> baconFile.close()
>>> baconFile = open('bacon.txt')
>>> content = baconFile.read()
>>> baconFile.close()
>>> print(content)
Hello world!
Bacon is not a vegetable.

  write() 返回值是被寫入字元的個數。檔案物件將跟蹤自身的位置,所以下次你呼叫 write的時候,它會在檔案末尾新增新的資料。

3.4 修改檔案

  呼叫 readlines() 方法,利用正則表達匹配修改內容或呼叫字串相關方法修改內容,重新寫入新檔案。備份原檔案,刪除原檔案。

3.5  colse() 方法關閉檔案

  如果用上下文管理器(with語句)則不需要呼叫colse()方法,否則應該在最後呼叫colse()方法。

>>> baconFile = open('bacon.txt', 'w')
>>> baconFile.write('Hello world!\n')
13
>>> baconFile.close()

4. 組織檔案

  參考自《Python程式設計快速上手  讓繁瑣工作自動化》

4.1 檢視目錄下所有檔案

  呼叫 os.listdir(path) 將返回傳入函式路徑(path)下所有檔名字串的列表,包含 path 中的每個檔案

>>> os.listdir('C:\\Windows\\System32')
['0409', '12520437.cpx', '12520850.cpx', '5U877.ax', 'aaclient.dll',
......
'xwtpdui.dll', 'xwtpw32.dll', 'zh-CN', 'zh-HK', 'zh-TW', 'zipfldr.dll']

4.2 遍歷目錄樹

  os.walk()在迴圈的每次迭代中,返回 3 個值:
  1. 當前資料夾名稱的字串。
  2. 當前資料夾中子資料夾的字串的列表。
  3. 當前資料夾中檔案的字串的列表。

import os

for folderName, subfolders, filenames in os.walk('C:\\delicious'):
    print('當前目錄(資料夾) folder 是 ' + folderName)
        for subfolder in subfolders:
            print('目錄下子資料夾 subflder 是 ' + folderName + ': ' + subfolder)
        for filename in filenames:
            print('目錄下檔案 file 是 ' + folderName + ': '+ filename)
        print('')    

  輸出如下,意思就是os.walk(),先搜尋資料夾(根目錄)下的所有子資料夾和檔案,而對根目錄下的每個子資料夾又做了同樣的事情,層次迭代,結果就是找到了所有的資料夾與檔案(即遍歷了目錄樹)

當前目錄資料夾 folder 是 C:\delicious
目錄下子資料夾 subflder 是 C:\delicious: cats
目錄下子資料夾 subflder 是 C:\delicious: walnut
目錄下檔案 file 是 C:\delicious: spam.txt

當前目錄資料夾 folder 是 C:\delicious\cats
目錄下檔案 file 是 C:\delicious\cats: catnames.txt
目錄下檔案 file 是 C:\delicious\cats: zophie.jpg

當前目錄資料夾 folder 是 C:\delicious\walnut
目錄下子資料夾 subflder 是 C:\delicious\walnut: waffles
當前目錄資料夾 folder 是 C:\delicious\walnut\waffles
目錄下檔案 file 是 C:\delicious\walnut\waffles: butter.txt.

4.3 shutil 模組

  4.3.1 複製檔案和資料夾

  呼叫 shutil.copy() 

>>> import shutil, os
>>> os.chdir('C:\\')
>>> shutil.copy('C:\\spam.txt', 'C:\\delicious')
'C:\\delicious\\spam.txt'
>>> shutil.copy('eggs.txt', 'C:\\delicious\\eggs2.txt')
'C:\\delicious\\eggs2.txt'

  4.3.2 檔案和資料夾的移動與改名

  呼叫 shutil.move(source, destination), 下面程式碼:

  1、如果資料夾 C:\eggs 中原來存在一個檔案 bacon.txt,它會被C:\\bacon.txt 覆蓋(替換)。

  2、目標資料夾 C:\\eggs 必須存在

  3、 shutil.move() 方法可以實現改檔名

>>> import shutil
>>> shutil.move('C:\\bacon.txt', 'C:\\eggs')
'C:\\eggs\\bacon.txt'
>>> shutil.move('C:\\bacon.txt', 'C:\\eggs\\new_bacon.txt')
'C:\\eggs\\new_bacon.txt'

  4.3.3永久刪除檔案和資料夾

  利用 os 模組中的函式,可以刪除一個檔案或一個空資料夾。但利用 shutil 模組,可以刪除一個資料夾及其所有的內容。

  • 調用 os.unlink(path)將刪除 path 處的檔案。
  • 呼叫 os.rmdir(path)將刪除 path 處的資料夾。該資料夾必須為空,其中沒有任何檔案和資料夾。
  • 呼叫 shutil.rmtree(path)將刪除 path 處的資料夾,它包含的所有檔案和資料夾都會被刪除。

  以上函式都是永久刪除用時要特別小心

import os
    for filename in os.listdir():
        if filename.endswith('.rxt'):
            #os.unlink(filename)
            print(filename)                

  4.3.4 用 send2trash 模組安全地刪除

  send2trash() 函式只能將檔案送到垃圾箱, 不能從中恢復檔案。

>>> import send2trash
>>> baconFile = open('bacon.txt', 'a') # creates the file
>>> baconFile.write('Bacon is not a vegetable.')
25
>>> baconFile.close()
>>> send2trash.send2trash('bacon.txt')

4.4.zipfile 模組

  4.4.1 讀取 ZIP 檔案  

>>> import zipfile, os
>>> os.chdir('C:\\') # move to the folder with example.zip
>>> exampleZip = zipfile.ZipFile('example.zip')
>>> exampleZip.namelist()
['spam.txt', 'cats/', 'cats/catnames.txt', 'cats/zophie.jpg']
>>> spamInfo = exampleZip.getinfo('spam.txt')
>>> spamInfo.file_size
13908
>>> spamInfo.compress_size
3828
>>> 'Compressed file is %sx smaller!' % (round(spamInfo.file_size / spamInfo.compress_size, 2))
'Compressed file is 3.63x smaller!'
>>> exampleZip.close()

  4.4.2 解壓檔案 

   執行這段程式碼後, example.zip 的內容將被解壓縮到 C:\

  或者, 你可以向extractall()傳遞的一個資料夾名稱,它將檔案解壓縮到那個資料夾, 而不是當前工作目錄。 

>>> import zipfile, os
>>> os.chdir('C:\\') # move to the folder with example.zip
>>> exampleZip = zipfile.ZipFile('example.zip')
>>> exampleZip.extractall()
>>> exampleZip.close()

  4.4.3 建立和新增到 ZIP 檔案

  下面程式碼 zipfile.ZipFile()方法中第二個引數 zipfile.ZIP_DEFLATED  是指定了 deflate 壓縮演算法,它對各種型別的資料都很有效

  這段程式碼將建立一個新的 ZIP 檔案, 名為 new.zip, 它包含 spam.txt 壓縮後的內容。

  和寫入檔案同樣,寫模式將擦除 ZIP 檔案中所有原有的內容。如果只是希望將檔案新增到原有的 ZIP 檔案中, 就要向 zipfile.ZipFile()傳入'a'作為第二個引數,以新增模式開啟 ZIP 檔案

>>> import zipfile
>>> newZip = zipfile.ZipFile('new.zip', 'w')
>>> newZip.write('spam.txt', compress_type=zipfile.ZIP_DEFLATED)
>>> newZip.close()