Python — pathlib函式庫 : 操作文字檔案與資料夾的好幫手-2

Sean Yeh
Python Everywhere -from Beginner to Advanced
16 min readAug 18, 2023

--

Rotterdam, Dutch, photo by Sean Yeh

在我們的生活中,無論是學習、工作,甚至是休閒娛樂,都會接觸到各式各樣的檔案和資料夾。想像一下,當您的電腦桌面或是手機相簿被數不清的文件和照片塞滿,是否也曾讓你感到焦頭爛額、煩躁不堪,不知從何下手整理呢?

像今天這個資訊洶湧的時代,上述的困擾是家常便飯。有時候,一大堆的數位文件和資料夾就像是一座無法逾越的高山,讓人頭疼不已,不知道該如何有效地將它們整理得井然有序。那麼,有沒有什麼工具或方法,可以幫助我們輕鬆且有效地管理這些檔案和資料夾呢?

答案是肯定的。Python的 pathlib 函式庫,就像是我們的得力助手,能夠輕鬆地幫助我們操作檔案和資料夾,節省我們的時間和精力。

在上一篇文章中,我們已經學會了如何使用 Path 類別來進行各式各樣的檔案與資料夾操作,例如把檔案路徑變成物件、查看檔案的各種資訊、建立資料夾,甚至是取得資料夾裡面的檔案清單等等。

今天,我們要以這些概念基礎,進一步挖掘更深層、更實用的功能!

顯示檔案列表

大家是否有這樣的經驗?使用電腦一段時間後,檔案不知不覺累積成山,每當想要找某個檔案時,總是要費盡九牛二虎之力。有些資料夾中還藏著好幾層的子資料夾,每打開一層,又出現新的子資料夾,就像是俄羅斯娃娃一樣,一層又一層,讓人眼花撩亂。

那麼,有沒有辦法可以讓我們一眼就看到資料夾中的所有檔案,而不用一層一層地點開呢?

A. 套件與命令方法

答案當然是有的!Python的 Path 套件正好可以幫到忙,而在這裡,我們還會介紹到幾個相當好用的方法:globrglob 以及 sorted

B. 概念

要達成這樣的目的,其實步驟也不複雜,大致上分為:

  1. 取得資料夾以及子資料夾的所有文字檔名稱
  2. 將檔案的名稱全部顯示出來

對於第一個步驟,你可能會好奇,要如何才能一次取得所有的子資料夾和檔案名稱呢?別擔心,這時候 rglob 方法就派上用場了!rglob 方法能夠協助我們取得資料夾內的所有檔案,包括子資料夾中的檔案。完成這步後,第二步驟就變得非常直觀和簡單了。

值得注意的是,我們不僅可以取得 .txt 的文字檔名稱,還可以透過相同的方法取得 .xlsx 的Excel檔案、.jpg.png 的圖片檔案,甚至是任何你想要的檔案格式。這樣,不論是報告、照片還是其他文件,我們都能快速地找到它們。

我們想要做的是,當我想要知道某一個A資料夾裡面有哪些B類型的檔案時,只要輸入A與B檔案類型,就可以依照這兩個資訊取出特定資料夾與子資料夾裡面的檔案。

基於這樣的概念,我們可以建立一個函式,這個函式可以傳入兩個變數,分別是「資料夾名稱」與「檔案類型」(也就是檔案的副檔名),函式會依照這兩個變數輸出資料夾與子資料夾裡面符合條件之檔案的列表。

C. 實作

接下來要實作的程式碼,其主要功能是在指定的資料夾(及其子資料夾)中尋找指定副檔名的所有檔案,並列出這些檔案的完整路徑。我們可以將它分成四個步驟:匯入套件、參數、函式、執行函式。以下分別針對這四個部分說明,並且再最後加上優化的步驟(步驟五):

1.匯入套件

首先,我們要匯入pathlib套件,主要用於處理檔案和資料夾的路徑操作。由於我們主要是要使用pathlib套件中的Path類別,因此可以透過下面方式匯入:

# 匯入
from pathlib import Path

2.參數

這裡設定了兩個參數:folder 是我們要搜尋的資料夾名稱,而 ext_value 是我們要搜尋的檔案的副檔名。

# 設定參數
folder = 'doc'
ext_value = "txt"

3.函式

接下來,我們定義了一個名為 show_file_lists 的函式,它接受兩個參數:要搜尋的資料夾名稱(folder)和檔案副檔名(ext_value)。

並且建立兩個變數message與filelist,分別為空白字串與空白list串列。

# 函式
def show_file_lists(folder, ext):
message = ""
filelist = []
# 尋找檔案
for path in Path(folder).rglob(str(f'*.{ext}')):
filelist.append(str(path))
# 整理檔案列表
for filename in sorted(filelist):
message = message + filename + "\\n"
# 回傳結果
return f"檔案數 = {str(len(filelist))}\\n{message}"

# 尋找檔案:透過 rglob 方法,我們可以找到資料夾及其所有子資料夾中的所有指定副檔名的檔案。這些檔案的路徑會被加入到先前設定的 filelist 串列列表中。

# 整理檔案列表:這部分是透過 sortedfilelist 中的所有檔案路徑按照字母順序排序,並將它們組合成一個長字符串 message,每個檔案路徑之間用換行符 \\n 分隔。

# 回傳結果:這行是將找到的檔案數量( len(filelist) )和所有檔案的路徑( message )一同回傳。

4.執行函式

這裡呼叫了 show_file_lists 函式並將結果存入 message 變數,然後使用print將結果顯示出來。

# 執行函式

message = show_file_lists(folder,ext_value)
print(message)

5.優化程式

以上是這個功能的基本做法,我們可以稍微的將它優化一下。

# 使用者輸入參數:我們可以將「設定參數」改為使用者輸入。在每次執行程式的時候,先詢問使用者想要查詢的資料夾以及檔案格式,使用者輸入完畢之後,再依照條件顯示檔案列表。

# 設定參數
folder = input("請輸入資料夾名稱")
ext_value = input("請輸入副檔名")
print(f"您要查詢的資料夾為: {folder}副檔名為: {ext_value}")

使用解析式列表 (list comprehension) :我們透過這種撰寫Python的方式來簡化程式碼並且提高效率。解析式列表的詳細說明可以參考這篇文章。

# 減少中間變數:另外,可以直接在返回的字串中整合檔案列表,減少中間變數的使用。

以下是優化後的函式:

# 函式
def show_file_lists(folder, ext):
filelist = [str(path) for path in Path(folder).rglob(f'*.{ext}')]
message = "\\n".join(sorted(filelist))
return f"檔案個數 = {len(filelist)}\\n{message}"

在這版程式碼中:

  1. 我們改用解析式列表來建立 filelist。( [str(path) for path in Path(folder).rglob(f'*.{ext}')]
  2. 使用 join 函式來組合檔案列表,這比使用 + 來串接字串更為效率。
  3. 直接在 f-string 中計算 filelist 的長度,而不是先將其轉換成字符串。

以上就是製作顯示檔案列表的完整步驟。下面是這次優化後全部的程式碼:

# 匯入
from pathlib import Path

# 設定參數
folder = input("請輸入資料夾名稱")
ext_value = input("請輸入副檔名")
print(f"您要查詢的資料夾為: {folder}副檔名為: {ext_value}")

# 函式
def show_file_lists(folder, ext):
filelist = [str(path) for path in Path(folder).rglob(f'*.{ext}')]
message = "\\n".join(sorted(filelist))
return f"檔案個數 = {len(filelist)}\\n{message}"

# 執行函式
message = show_file_lists(folder,ext_value)
print(message)

# 再次優化:上面的程式碼已經相對的簡潔。但我們還可以進一步優化它,讓結構更為清晰和模組化。 例如,我們可以使用主函式 (main()) 來架構程式的流程,並且將輸入的部分包裝為一個函式,使其更加模組化。

依照上面的原則,程式碼可再次優化為下面的樣子:

# 匯入
from pathlib import Path

# get_inputs函式
def get_inputs():
"""取得使用者輸入的資料夾名稱和副檔名"""
folder = input("請輸入資料夾名稱: ")
ext_value = input("請輸入副檔名: ")
print(f"您要查詢的資料夾為:{folder} 副檔名為:{ext_value}")
return folder, ext_value

# show_file_lists函式
def show_file_lists(folder, ext):
"""根據資料夾和副檔名顯示檔案列表"""
filelist = [str(path) for path in Path(folder).rglob(f'*.{ext}')]
message = "\\\\n".join(sorted(filelist))
return f"檔案個數 = {len(filelist)}\\\\n{message}"

# main主函式
def main():
"""主函式"""
folder, ext_value = get_inputs()
message = show_file_lists(folder, ext_value)
print(message)

# 執行主函式
if __name__ == "__main__":
main()

在程式碼中,我們使用 if __name__ == "__main__": 來確保只有當這個程式腳本被直接執行時,main() 函式才會被呼叫。如果這程式個腳本被其他程式當作模組引入時, main() 函式不會被自動執行,如此可以增加程式的模組化和重複使用性。

顯示檔案容量

讓Python幫你找出檔案的「真實身材」

大家回想一下,是不是每次當我們的電腦或手機提示「儲存空間不足」時,就開始緊張地找尋並刪除大量的檔案,以釋放一些空間呢?當面對一堆檔案時,您是否想過:「這些檔案到底佔了多大的容量啊?」。

其實,這就像是我們在出國打包行李箱前夕,必須要衡量每樣物品的重要性和它們總共佔的體積,好決定該帶什麼、該留下什麼,而不會讓行李箱超重。

那麼,在數位的領域裡,有沒有一個簡單的方法,可以快速查看手邊的檔案,並了解它們的「大小」,從而更聰明地管理我們的資料呢?

方法是有的。在此,我們要再次利用Python的 Path 套件,探索每個檔案的「真實身材」,讓我們不再對儲存空間感到手足無措。開始吧!

A. 套件與命令方法

要完成這項任務,我們需要使用一些特定的套件和方法。其中,最重要的仍然當屬 Path 類別, 另外,實作這個範例將會使用到的相關方法有rglobsorted以及 state 等。

rglob可以讓我們取得特定資料夾與子資料夾,顯示檔案的容量大小可以利用p.state().st_size

B. 概念

進行這項任務,基本上可以分為三個主要步驟:

  1. 探索特定資料夾與子資料夾,找到裡面的所有檔案。
  2. 了解每個檔案的大小。
  3. 將所有檔案大小加總起來,得到總容量。

具體要如何操作呢?讓我們繼續看下去。

C. 實作

接下來要實作的程式碼,會先取得特定資料夾與子資料夾的所有檔案,取得並且顯示每個檔案的檔案大小,然後將所有檔案大小加總起來。我們可以將它分成四個步驟:匯入套件、參數、函式、執行函式。以下分別針對這三個部分說明:

1.匯入套件

首先,我們依然要要從 pathlib 中引入 Path 類別,它是我們進行檔案操作的得力助手:

# 匯入
from pathlib import Path

2.參數

跟前面的例子一樣,透過參數,我們簡單設定一下要查詢的資料夾和副檔名:

# 參數
folder = "doc"
ext_value = "txt"

3.函式

在這裡,我們建立一個函式來完成檔案大小的取得和顯示。這函式會透過loop巡迴指定的資料夾,找到所有的檔案,並印出它們的大小:

# 函式
def show_files_size(folder,ext):
allsize = 0
filelist = []
# 尋找檔案
for path in Path(folder).rglob(str(f'*.{ext}')):
filelist.append(str(path))
# 整理檔案列表
for filename in sorted(filelist):
# 取得檔案大小
size = Path(filename).stat().st_size
print(f"{filename} = {str(size)} Byte")
# 加總檔案大小
allsize = allsize + size
# 回傳結果
return f"allsize = {str(allsize)} Byte"

具體來說,該函式的功能如下:

  1. 初始化參數:函式開始時,首先初始化了兩個變數。allsize 是用來儲存所有檔案的總大小的,filelist 是用來儲存找到的檔案路徑的串列。
  2. 尋找檔案:透過 Path(folder).rglob(str(f'*.{ext}')) 這段程式碼,函式會尋找指定資料夾及其所有子資料夾中具有特定副檔名的所有檔案。
  3. 整理檔案列表:在找到所有符合條件的檔案後,函式會對這些檔案路徑進行排序,並對每個檔案進行以下操作:
  • 使用 Path(filename).stat().st_size 獲取該檔案的大小。
  • 使用print印出該檔案的路徑和大小。
  • 將該檔案的大小加到 allsize 這個變數上。

4. 回傳結果:最後,函式會回傳一個字串,該字串表示所有找到的檔案的總大小。

4.執行函式

最後,只要呼叫我們前面定義的函式show_files_size,就能看到結果。

# 執行函式
message = show_files_size(folder,ext_value)
print(message)

5.優化程式

寫程式就像是烹調,我們有時候需要在完成基礎的功能後,進一步的調味,使其更加美味、吸引人。以目前這個「顯示檔案大小程式」來說,顯示的單位只用「Byte」,當檔案大小非常大時,「Byte」這個單位會讓數字看起來很冗長、不直觀。對於使用者來說,可能就像是嚐到了沒調味的食物,感覺缺了點什麼。所以,我們考慮進一步優化它,將其轉為更直觀的KB、MB、GB、TB等。

由於資料的存儲單位是以1024為基礎,進行進位的。也就是說,1024 Byte 就是1KB,1024 KB就是1MB,以此類推。根據這個原理,我們加入了一個自訂的新函式 format_bytes,它可以讓我們更方便地轉換這些單位。

def format_bytes(size):
units = ["Byte","KB","MB","GB","TB","PB"]
n = 0
while size > 1024:
size = size / 1024.0
n = n + 1
if n > 0:
return f"{size:.1f} {units[n]}"
else:
return f"{int(size)} {units[n]}"

format_bytes 函式的其目的是將檔案的大小單位從單純的「Byte」轉換成更適合人類閱讀的單位,如「KB」、「MB」、「GB」等。在函式裡面:

  • units 是一個list串列,裡面存儲了各種單位,從「Byte」到「PB」。
  • 並且使用while 迴圈,它會持續執行,直到 size 小於或等於1024為止。每次迴圈,size 會被除以1024,而 n 會加1,如此我們會在 units 中上移一個單位。
  • 執行到最後,函式最後會回傳經過格式化的大小,以及相對應的單位。如果單位不是「Byte」,則會保留一位小數;否則,將直接回傳整數值。

接著,對 show_files_size 函式進行了一些調整,讓 format_bytes 這個函式可以被使用到。

# 函式
def show_files_size(folder,ext):
message=""
filelist = []
allsize = 0
for path in Path(folder).rglob(str(f'*.{ext}')):
filelist.append(str(path))

for filename in sorted(filelist):
size = Path(filename).stat().st_size
message = message + filename + ": " + format_bytes(size)+"\\n"
allsize = allsize+ size
filesize = "檔案容量總和 = " + format_bytes(allsize) + "\\n"
filesize = filesize + "檔案個數 = " + str(len(filelist)) + "\\n"
message = message + filesize
return message

因此,我們在使用 Path(filename).stat().st_size 獲取檔案的大小的地方,加入 format_bytes 函式。當 show_files_size 需要將檔案大小從Byte轉換成其他單位時,它會呼叫 format_bytes 函式進行轉換。

現在,透過這支程式,當我們想知道某個資料夾中的檔案大小時,不僅可以輕鬆掌握資料夾中每個檔案的大小,其結果不僅僅正確,還更加友善、易懂。

結語:從亂到整,一切就在手中

在這資訊不斷洶湧而來的時代,我們每天都會產生資料與檔案,在我們的電腦裡,也幾乎都會積壓大量的資料與檔案。有時候,這些檔案就像是一堆散落的拼圖,看似毫無章法。但有了Python的 pathlib 函式庫,它不僅能夠幫我們「找回資料的秩序」,更能讓我們的日常檔案管理工作變得輕鬆許多。

透過這篇文章,從最基礎的概念開始,我們逐步深入到具體的實作,並在最後進行了優化。我們學到了如何「顯示檔案列表」,不讓任何一個檔案能夠逃脫我們的眼光。更進一步,我們也能夠「顯示檔案容量」,讓我們能精準地掌握每個檔案的大小,確保電腦的儲存空間使用得宜。

希望透過這篇文章,你能夠更有自信地使用Python,讓它成為您的得力助手,協助您管理和掌握自己的檔案世界。想要「找回資料的秩序」就不再是個問題。

延伸閱讀

--

--

Sean Yeh
Python Everywhere -from Beginner to Advanced

# Taipei, Internet Digital Advertising,透過寫作讓我們回想過去、理解現在並思考未來。並樂於分享,這才是最大贏家。