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

Sean Yeh
Python Everywhere -from Beginner to Advanced
15 min readAug 29, 2023

--

Leende, Noord Brabant, Dutch, photo by Sean Yeh

是否曾有過這樣的經驗?明明知道檔案在電腦裡,但卻不記得它的完整名稱,只有片段的印象、只記得檔案的部分名稱。或者,工作上需要批次建立大量資料夾,例如依照一份名單上的名字,分別建立每個人的資料夾,但每次都得一個一個的建立。面對如此繁瑣的步驟,是不是覺得有點頭疼?這時候,可能就會想:「該不會有工具可以幫助我們解決這些問題吧?」

沒錯,Python就是您的得力助手。在這篇文章中,我們將探討如何透過Python的pathlib函式庫,輕鬆找回那些「不知道名稱,只有印象」的檔案,以及自動化地完成批次建立資料夾的任務,讓原本繁瑣的步驟變得輕鬆簡單。

讓我們一起來探索,如何利用Python的pathlib函式庫,讓檔案和資料夾的操作變得更加輕鬆。

搜尋檔案名稱

我們都有過這樣的經驗,知道檔案在某個地方,但只記得它名稱的一部分。這時,想像一下,如果只要提供這些斷簡殘篇,電腦就能馬上在指定資料夾裡找出所有符合的檔案名稱,那是不是感覺超級方便?透過pathlib函式庫可以做到這點,接下來,讓我們開始說明該如何做到。

A. 套件與命令方法

首先,依舊會使用到Path套件,它是這次任務的核心,協助我們處理路徑和檔案相關的操作。再來,是rglob()命令,可以幫我們搜尋指定資料夾及其子資料夾中的所有檔案。另外,有時候我們只想知道某些特定的名稱或片段是否出現在檔案中,這時count()命令就派上用場,它可以助我們計算特定字串在文本中出現的次數。

總之,這些工具和命令組合起來,能使搜尋過程更為簡單和高效。下表整理了即將使用的套件和命令:

B. 概念

想要找回那個「似曾相識」的檔案名稱時,該如何下手呢?其實,答案就在資料夾的檔案名稱裡。

要達到這樣子的功能,必須讓電腦取得特定資料夾與子資料夾的所有檔案名稱。首先,得讓電腦拜訪特定的資料夾及其子資料夾,取得每一個角落裡的檔案名稱。接著,就是比對了。電腦會根據使用者提供的部分名稱訊息,進行逐一比對。當它發現有檔案名稱中包含了先前提供的字串時,它就會告訴我們完整的檔案名稱和位置。

# count的用法

您可能會好奇,這樣的比對機制是怎麼運作的?其實,在這裡使用了一個叫做 count 的方法。這是一個很好用的工具,尤其當您想要知道某個字串出現了多少次的時候。例如,當我們用 'abcmarket.txt'.count('abc'),它就會告訴我們 abc 出現了一次。反之,如果沒有找到,就會顯示0次。

讓我們再看一下範例:

text = 'abcmarket.txt'
word1 = 'abc'
word2 = 'mak'

count1 = text.count(word1)
print(f'找到word1:{count1}個')

count2 = text.count(word2)
print(f'找到word2:{count2}個')

透過上述的程式碼,可以清楚看到,當檔案名稱 text 中出現了我們想要找的 word1word2count 就會給我們肯定的答案。

C. 實作

經過前面的概念說明,是否已經讓您躍躍欲試,希望親手實作看看呢?現在就一起來一探究竟。我們要利用以前學到的rglob(),與今天介紹的count()命令,來撰寫搜尋檔名的程式。

1.匯入套件

在此依舊要匯入pathlib套件。由於我們要使用pathlib套件中的Path類別,於是透過下面方式匯入:

# 匯入
from pathlib import Path

2.參數

接下來設定參數。這裡設定了兩個參數:folder 是我們要搜尋的資料夾名稱,而 search_value則是要搜尋的字串。

# 設定參數
folder = 'doc'
search_value = "test"

3.函式

接下來是主角,findfilename 函式。這個函式的作用是,根據提供的資料夾和部分檔名,幫我們找出所有符合條件的檔案名稱。

# 函式
def findfilename(folder,findword):
cnt = 0
msg = ''
filelist =[]

for p in Path(folder).rglob("*.*"):
if p.name[0] != ".":
filelist.append(str(p))

for filename in filelist:
if filename.count(findword) > 0:
msg += filename + "\\n"
cnt += 1
msg = "符合條件的檔案個數 = " + str(cnt) + "\\n" + msg
return msg

首先,透過 rglob("*.*") 取得所有檔案名稱,並存入 filelist。請注意,這裡我們放了一個判斷式: if p.name[0] != "." 這是為了過濾掉系統生成的隱藏檔案。

接著,只需透過簡單的 count() 方法,檢查每個檔案名稱是否包含我們提供的部分檔名 findword

最後,函式將返回所有符合條件的檔案名稱,以及這些檔案的總數。

4.執行函式

最後一步,就是執行函式並列印出結果:

# 執行函式
msg = findfilename(folder,search_value)
print(msg)

這樣,只要簡單的幾行程式,就可以輕鬆找出所有想要的檔案名稱。

5.優化程式

接下來,可以針對上面的程式碼進行優化。以下是程式碼優化的方式:

  1. 參數不在固定,改由透過使用者自行輸入。
  2. 移除了中間的 filelist。可以只在一次迴圈內完成所有操作,無需先存儲所有的檔案名稱。
  3. 直接使用 Path 對象進行操作,避免不必要的 str 轉換。
  4. += 進行增加,讓程式碼更簡潔。
from pathlib import Path

# 輸入參數
folder =input('請輸入資料夾名稱')
search_value = input('請輸入關鍵字')

# 函式
def findfilename(folder, findword):
count = 0
msg = ''
for p in Path(folder).rglob("*.*"):
if p.name[0] != "." and findword in p.name:
msg += str(p) + "\\n"
count += 1
msg = f"符合條件的檔案個數 = {count}\\n{msg}"
return msg

# 執行函式
msg = findfilename(folder, search_value)
print(msg)

此外,我們也可以將程式碼以物件導向程式設計 (OOP) 的方式表現。這樣能夠使程式更加模組化和具備可擴充性。

from pathlib import Path

class FileFinder:
def __init__(self, folder: str, search_value: str):
self.folder = folder
self.search_value = search_value
self.matched_files = []
def search_files(self):
for p in Path(self.folder).rglob("*.*"):
if p.name[0] != "." and self.search_value in p.name:
self.matched_files.append(p)
def display_results(self):
count = len(self.matched_files)
msg = '\\n'.join([str(p) for p in self.matched_files])
print(f"符合條件的檔案個數 = {count}\\n{msg}")
def run(self):
self.search_files()
self.display_results()
# 設定參數
folder =input('請輸入資料夾名稱')
search_value = input('請輸入關鍵字')
finder = FileFinder(folder, search_value)
finder.run()

利用csv檔案,批次建立資料夾

在現今的企業環境中,效率是每位員工追求的目標。想像一下,若公司要舉辦大型的教育訓練,涉及的員工多達100位。如果要手動為每位員工建立專屬的資料夾,不僅耗時,也容易出錯。那麼,是否有更聰明的方法,讓我們充分利用電腦的力量,輕鬆完成這樣看似繁瑣的任務呢?

在本段落中,將會探討如何透過Python,結合csv檔案,輕鬆完成這項任務,讓例行公事不再是負擔。

A. 套件與命令方法

在Python中,當我們要處理csv格式的檔案時,就會想到csv套件。由於它是標準函式庫的一部分,只需要簡單的import語法,就能將它引入程式中。

而對於檔案的開啟,通常會使用open命令,這可以讓我們輕鬆地讀取或寫入檔案。

B. 概念

假設手上有一份詳細的員工名冊時,並且它是簡單而且結構清楚的CSV檔。當電腦拿到這份CSV檔案後,會先開啟這份檔案,逐行讀取裡面的內容。每讀取到一位員工的名稱,它就會立刻在指定位置建立一個對應的資料夾。透過這樣的方式,電腦能夠迅速且精確地為每位員工建立屬於他們自己的專屬資料夾,確保資料的整齊與管理的便利性。

#open用法

當我們使用Path套件操作檔案時,可以使用open命令開啟它:

f = Path(檔案路徑名稱).open(encoding="UTF-8")

如上面的語法,當使用open開啟檔案時,並在其中放入 encoding="UTF-8" 的參數,這樣子就會載入UTF-8編碼的CSV檔案。

#取得CSV檔案的各個元素

在讀取了csv檔案之後,可以透過下面的方式取得它的每一個元素:

import csv
data = csv.reader(f)
for row in data:
for value in row:
print(value)

結合上述兩個概念,可以透過下面這方式讀取csv檔案中的所有資料:

infile = "doc/namelist.csv"
f = Path(infile).open(encoding="UTF-8")
data = csv.reader(f)
for row in data:
for value in row:
print(value)

為了使程式更加周延,我們可以使用tryexcept來處理可能出現的錯誤:

try:
可能發生錯誤的處理
except:
發生錯誤時的處理

上面的程式修改之後,就會像下面這一段:

infile = "doc/namelist.csv"
try:
f = Path(infile).open(encoding="UTF-8")
data = csv.reader(f)
for row in data:
for value in row:
print(value)
except:
print("檔案不存在,無法讀取檔案")

使用此方法,可以讓程式在出現錯誤時,給出有用的提示,而不是直接當掉。

C. 實作

1.匯入套件

首先,我們匯入必要的套件pathlib中的Pathcsv

# 匯入
from pathlib import Path
import csv

2.參數

並且設定一些基礎參數:

# 參數
infile = "doc/namelist.csv"
value1 = "doc/outputfolder"

3.函式

在這裡,建立了一個函式makefolders,它可以從csv檔案中讀取資料,並在指定的資料夾中建立新的資料夾:

# 函式
def makefolders(readfile,savefolder):
try:
msg = ''
Path(savefolder).mkdir(exist_ok=True)
f = Path(infile).open(encoding="UTF-8")
csvdata = csv.reader(f)
for row in csvdata:
for foldername in row:
newfolder = Path(savefolder).joinpath(foldername)
newfolder.mkdir(exist_ok=True)
msg += "在" + savefolder + "建立了" + foldername + "了。\\n"
return msg

except:
return readfile + " :無法載入檔案。"

4.執行函式

直接呼叫上述的函式,開始建立資料夾。

# 執行函式
msg = makefolders(infile,value1)
print(msg)

5.優化程式

我們可以透過以下方式優化程式碼:

  1. 使用 with 語句自動關閉檔案,用以避免檔案處於開啟狀態太久。
  2. 移除 msg 字串的連續拼接,改用 list comprehension 來進行格式化。
  3. 優化錯誤處理程序,捕捉具體的異常,使錯誤訊息更具意義。
  4. 避免使用全域變數 infile,直接使用函式參數 readfile

下面是依照上面原則優化後的程式碼:

from pathlib import Path
import csv

def makefolders(readfile, savefolder):
msgs = []
Path(savefolder).mkdir(exist_ok=True)
try:
with Path(readfile).open(encoding="UTF-8") as f:
csvdata = csv.reader(f)
for row in csvdata:
for foldername in row:
newfolder = Path(savefolder).joinpath(foldername)
newfolder.mkdir(exist_ok=True)
msgs.append(f"在{savefolder}建立{foldername}了。")
except FileNotFoundError:
return f"{readfile} :無法載入檔案。"
except Exception as e:
return str(e)
return '\\\\n'.join(msgs)

# 參數
infile = "doc/namelist.csv"
value1 = "doc/outputfolder"

# 執行函式
msg = makefolders(infile, value1)
print(msg)

經過這些修改,使得程式碼更為簡潔、更具有可讀性,並且更能確實地處理潛在的錯誤。

或者也可以使用物件導向程式設計 (OOP)的方式優化程式碼。以下是使用物件導向程式設計方式重構後的程式碼:

  1. 建立了一個 FolderManager 類別來處理所有與文件和資料夾相關的操作。
  2. __init__ 方法中,初始化必要的參數。
  3. makefolders 方法現在成為類別的一個方法,而不再是獨立的函式。
from pathlib import Path
import csv

class FolderManager:
def __init__(self, readfile, savefolder):
self.readfile = readfile
self.savefolder = savefolder
def makefolders(self):
msgs = []
Path(self.savefolder).mkdir(exist_ok=True)
try:
with Path(self.readfile).open(encoding="UTF-8") as f:
csvdata = csv.reader(f)
for row in csvdata:
for foldername in row:
newfolder = Path(self.savefolder).joinpath(foldername)
newfolder.mkdir(exist_ok=True)
msgs.append(f"在{self.savefolder}建立{foldername}了。")
except FileNotFoundError:
return f"{self.readfile} :無法載入檔案。"
except Exception as e:
return str(e)
return '\\\\n'.join(msgs)
# 參數
infile = "doc/namelist.csv"
value1 = "doc/outputfolder"
# 初始化物件並執行方法
folder_mgr = FolderManager(infile, value1)
msg = folder_mgr.makefolders()
print(msg)

這種物件導向程式設計的方式使得程式碼結構更為清晰,如將來需要增加更多的方法或屬性。目前這種寫法也提供了更多的擴充性,

結語

隨著時間的推進,我們一同探索了Python的pathlib函式庫,並深化了對它的認識。從一開始的迷茫和不確定,到如今能夠輕易地操作檔案和資料夾,每一次的嘗試都是成長的軌跡。

回顧過去的我們,在面對眾多的檔案和資料夾時,可能感到手足無措。但現在,只需幾行程式碼,就能將繁瑣的工作迎刃而解。這樣的便利不只節省了時間,更讓工作流程變得更為精準、更加高效。

期望這篇文章為您開啟一扇新的大門,引領進一步探索Python的知識寶藏。憑藉著Python這得力的夥伴,未來的旅程雖長,我們的道路卻將更加光明。

--

--

Sean Yeh
Python Everywhere -from Beginner to Advanced

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