文章目錄
- 1 pathlib 庫簡介
- 1.1 為什麼要使用 pathlib?
- 2 基本操作介紹
- 2.1 路徑操作
- 2.1.1 導入和創建 Path 對象
- 2.1.2 路徑拼接和解析
- 2.1.3 路徑判斷和解析
- 2.2 目錄操作
- 2.2.1 創建和刪除目錄
- 2.2.2 遍歷目錄內容
- 2.3 文件操作
- 2.3.1 文件讀寫
- 2.3.2 文件屬性和操作
- 3 常用場景演示
- 3.1 常用場景羅列
- 3.2 彙總成一個類+main()演示
- 5 pathlib 與傳統 os.path 的對比
- 6 總結
1 pathlib 庫簡介
pathlib 是 Python 3.4 及更高版本中的標準庫,它採用面向對象的方法來處理文件系統路徑。與傳統的 os.path 模塊不同,pathlib 將路徑表示為對象而不是字符串,使得路徑操作更加直觀和 Pythonic。
1.1 為什麼要使用 pathlib?
傳統上,Python 使用 os.path 模塊處理路徑,它將路徑作為字符串處理,這種方式存在一些問題:
- 操作分散在不同模塊(os、os.path、glob、shutil 等)
- 代碼跨平台兼容性差
- 路徑操作不夠直觀
pathlib 解決了這些問題,它提供統一的 API,支持跨平台路徑處理,並且代碼更易讀。
2 基本操作介紹
2.1 路徑操作
2.1.1 導入和創建 Path 對象
from pathlib import Path
# 創建Path對象的幾種方式
current_path = Path() # 當前目錄
relative_path = Path('folder/subfolder') # 相對路徑
absolute_path = Path('/home/user/documents') # 絕對路徑(Linux/Mac)
windows_path = Path('C:/Users/Name/Documents') # 絕對路徑(Windows)
# 使用類方法
current_dir = Path.cwd() # 當前工作目錄
home_dir = Path.home() # 用户主目錄
2.1.2 路徑拼接和解析
pathlib 使用斜槓運算符 / 來拼接路徑,這比傳統的 os.path.join() 更直觀:
# 路徑拼接
base_dir = Path('/base/demo')
sub_path = base_dir / 'subfolder' / 'file.txt'
print(sub_path) # 輸出: /base/demo/subfolder/file.txt
# 解析路徑組成部分
path = Path('D:/Users/base_demo/demo.py')
print(path.name) # 文件名(含後綴): demo.py
print(path.stem) # 文件名(不含後綴): demo
print(path.suffix) # 文件後綴: .py
print(path.parent) # 父目錄: D:/Users/base_demo
print(path.anchor) # 錨點(驅動器): D:
2.1.3 路徑判斷和解析
path = Path('/path/to/file.txt')
# 路徑判斷
print(path.exists()) # 路徑是否存在
print(path.is_file()) # 是否是文件
print(path.is_dir()) # 是否是目錄
print(path.is_absolute()) # 是否是絕對路徑
# 路徑解析
print(path.resolve()) # 獲取絕對路徑,解析任何符號鏈接
print(path.as_uri()) # 將路徑表示為file URL
2.2 目錄操作
2.2.1 創建和刪除目錄
# 創建目錄
new_dir = Path('new_directory')
new_dir.mkdir(exist_ok=True) # exist_ok=True表示目錄已存在時不報錯
# 創建多級目錄
deep_dir = Path('path/to/deep/directory')
deep_dir.mkdir(parents=True, exist_ok=True) # parents=True創建所有必要的父目錄
# 刪除目錄(目錄必須為空)
empty_dir = Path('empty_directory')
empty_dir.rmdir()
2.2.2 遍歷目錄內容
path = Path('.')
# 遍歷直接子項
for item in path.iterdir():
if item.is_file():
print(f"文件: {item.name}")
elif item.is_dir():
print(f"目錄: {item.name}")
# 使用glob模式匹配
for py_file in path.glob('*.py'):
print(f"Python文件: {py_file}")
# 遞歸匹配(搜索子目錄)
for py_file in path.rglob('*.py'):
print(f"Python文件: {py_file}")
2.3 文件操作
2.3.1 文件讀寫
pathlib 提供了便捷的文件讀寫方法:
file_path = Path('example.txt')
# 寫入文件
file_path.write_text('Hello, World!') # 寫入文本
# 讀取文件
content = file_path.read_text()
print(content)
# 二進制讀寫
data = b'Binary data'
file_path.write_bytes(data) # 寫入二進制數據
binary_content = file_path.read_bytes() # 讀取二進制數據
# 使用open方法(更細粒度的控制)
with file_path.open('a') as f: # 追加模式
f.write('\nAppended text')
2.3.2 文件屬性和操作
file_path = Path('example.txt')
# 獲取文件屬性
stat_info = file_path.stat()
print(f"文件大小: {stat_info.st_size} 字節")
print(f"創建時間: {stat_info.st_ctime}")
print(f"修改時間: {stat_info.st_mtime}")
# 文件操作
new_path = file_path.rename('new_name.txt') # 重命名
copied_path = new_path.replace('backup.txt') # 移動/複製(會覆蓋已存在文件)
# 刪除文件
file_path.unlink(missing_ok=True) # missing_ok=True表示文件不存在時不報錯
2.4 彙總
|
操作類別
|
具體操作
|
pathlib方法/示例
|
關鍵點/注意事項
|
|
路徑操作
|
創建路徑對象
|
|
使用/運算符拼接路徑,直觀且跨平台。
|
|
路徑操作
|
獲取路徑組成部分
|
|
這些是屬性,直接調用即可。
|
|
路徑操作
|
路徑分解與拼接
|
|
parts屬性便於分析路徑結構。
|
|
路徑操作
|
檢查路徑屬性
|
|
在進行操作前進行檢查可使代碼更健壯。
|
|
路徑操作
|
轉換路徑
|
|
resolve()會消除路徑中的"…"等符號。
|
|
文件操作
|
創建文件
|
|
如果文件已存在,touch會更新其修改時間。也可用於創建空文件。
|
|
文件操作
|
讀取文件內容
|
|
一次性讀取全部內容。對於大文件,建議使用open方式逐行讀取。
|
|
文件操作
|
寫入文件內容
|
|
會覆蓋文件原有內容。若需追加,需使用open模式。
|
|
文件操作
|
複製文件
|
|
複製文件。創建源文件的一個副本,原始文件保留。shutil.copy2複製文件內容及所有元數據(如創建時間、修改時間)。
|
|
文件操作
|
移動/重命名文件
|
|
移動/重命名文件或目錄。原始文件在操作後會被刪除。
|
|
文件操作
|
刪除文件
|
|
設置missing_ok=True時,若文件不存在則忽略錯誤。
|
|
目錄操作
|
創建目錄(單級)
|
|
默認只能創建末級目錄,若父目錄不存在會報錯。
|
|
目錄操作
|
創建目錄(多級)
|
|
parents=True創建父目錄;exist_ok=True忽略目錄已存在的情況。
|
|
目錄操作
|
刪除目錄
|
|
只能刪除空目錄。
|
|
目錄操作
|
遍歷目錄內容
|
|
返回一個生成器,包含該目錄下的所有文件和子目錄的Path對象。
|
|
目錄操作
|
遞歸遍歷與文件匹配
|
|
使用通配符模式匹配文件,非常強大。
|
|
獲取信息
|
獲取文件狀態信息
|
|
返回一個包含文件大小(st_size)、修改時間(st_mtime)等詳細信息的對象。
|
3 常用場景演示
3.1 常用場景羅列
|
場景類別
|
場景描述
|
預期輸出
|
|
文件夾列表獲取 |
獲取直接子文件夾名稱
|
返回直接子文件夾的名稱列表
|
|
獲取所有子文件夾路徑
|
返回所有子文件夾的 Path 對象列表
|
|
|
文件列表獲取 |
獲取直接子文件名稱(含擴展名)
|
返回直接子文件的名稱列表(帶後綴)
|
|
獲取所有子文件名稱(無擴展名)
|
返回所有子文件的名稱列表(無後綴)
|
|
|
獲取所有子文件路徑
|
返回所有子文件的 Path 對象列表
|
|
|
條件篩選 |
按字符串邏輯與(AND)篩選文件夾路徑
|
返回滿足所有篩選條件的文件夾路徑列表
|
|
按字符串邏輯或(OR)篩選文件路徑
|
返回滿足任一篩選條件的文件路徑列表
|
|
|
文件夾操作 |
清空目標文件夾
|
刪除目標文件夾內所有內容並重建空文件夾
|
|
複製目錄結構
|
在目標位置創建與源目錄相同的空文件夾結構
|
|
|
查找葉子目錄
|
返回不包含任何子目錄的最底層目錄(葉子目錄)列表
|
|
|
文件操作 |
複製指定格式文件
|
將源目錄(及子目錄)中所有指定格式的文件複製到目標文件夾
|
|
複製目錄和文件
|
先複製目錄結構,再將指定格式的文件複製到對應目錄
|
|
|
複製並重命名文件
|
將文件複製到目標文件夾,並按新名稱保存(保留原擴展名)
|
*使用技巧與説明:
- 路徑對象與字符串:獲取路徑列表的方法返回的是
pathlib.Path對象列表,在進行字符串操作(如判斷是否包含特定字符)時,常需要使用str(item)轉換。若最終需要字符串路徑,可以這樣處理:[str(path) for path in path_list]。 - 過濾邏輯:
logical參數提供了靈活的篩選方式。例如,查找包含"報告"或"總結"的文件夾,可使用邏輯或;而查找同時包含"2024"和"月度報告"的文件夾,則需使用邏輯與。 - 遞歸搜索:使用
rglob('*')的方法會遍歷指定路徑下的所有子目錄,適合需要對整個目錄樹進行操作的場景。
3.2 彙總成一個類+main()演示
下面是一個完整的 FileIO 類,封裝了常見的文件路徑操作,同時對3.1羅列的場景進行了簡單的示例:
from pathlib import Path
import shutil
import time
class FileIO:
"""
使用 pathlib 封裝常見文件路徑操作的實用類
該類提供了一系列用於文件和目錄操作的方法,包括遍歷、篩選、清空文件夾等功能。
所有路徑操作均基於 pathlib 庫,確保跨平台兼容性。
"""
def __init__(self, source_folder: None |str | Path, target_folder:None |str | Path) -> None:
"""
初始化 FileIO 實例
Args:
source_folder: 源目錄路徑,可以是字符串或 Path 對象
"""
self.source_folder = Path(source_folder) # 保證路徑是 Path 格式
self.target_folder = Path(target_folder) #目標文件
if not self.target_folder.exists(): #目標文件夾不存在時,新建
self.target_folder.mkdir(parents=True, exist_ok=True)
def list_folder_names(self, recursive: bool = True) -> list[str]:
"""
獲取文件夾名稱列表
Args:
recursive: 是否遞歸獲取所有子文件夾,默認為 True
True - 遞歸獲取所有子文件夾
False - 只獲取直接子文件夾
Returns:
文件夾名稱列表
"""
folder_path = self.source_folder
folder_names = [item.name
for item in (folder_path.rglob('*') if recursive else folder_path.iterdir()) #iterdir()比glob('*')更高效,
if item.is_dir()]
return folder_names
def list_folder_paths(self, recursive: bool = True) -> list[Path]:
"""
獲取文件夾路徑列表
Args:
recursive: 是否遞歸獲取所有子文件夾路徑
Returns:
Path 對象列表,包含所有匹配的文件夾路徑
"""
folder_path = self.source_folder
folder_paths = [item
for item in (folder_path.rglob('*') if recursive else folder_path.iterdir())
if item.is_dir()]
return folder_paths
def list_file_names(self, recursive: bool = True, include_extension: bool = True) -> list[str]:
"""
獲取文件名列表
Args:
recursive: 是否遞歸獲取所有子文件
include_extension: 是否包含文件擴展名
True - 包含擴展名 (e.g., "document.txt")
False - 不包含擴展名 (e.g., "document")
Returns:
文件名列表
"""
folder_path = self.source_folder
file_names = [
item.name if include_extension else item.stem
for item in (folder_path.rglob('*') if recursive else folder_path.iterdir())
if item.is_file()
]
return file_names
def list_file_paths(self, recursive: bool = True) -> list[Path]:
"""
獲取文件路徑列表
Args:
recursive: 是否遞歸獲取所有子文件路徑
Returns:
Path 對象列表,包含所有匹配的文件路徑
"""
folder_path = self.source_folder
file_paths = [item
for item in (folder_path.rglob('*') if recursive else folder_path.iterdir())
if item.is_file()]
return file_paths
@staticmethod
def filter_by_strings(items: list[str] | list[Path], target_strings: list[str], logical_or: bool = True) -> list[str]:
"""
根據字符串列表篩選項目
Args:
items: 要篩選的項目列表
target_strings: 目標字符串列表,用於篩選條件
logical_or: 邏輯操作類型
True - 邏輯或(包含任意一個目標字符串即匹配)
False - 邏輯與(包含所有目標字符串才匹配)
Returns:
篩選後的項目列表
"""
if logical_or: # 邏輯OR:包含任意一個字符串即可
filtered_items = [item
for item in items
if any(target_str in str(item) for target_str in target_strings)
]
else: # 邏輯AND:包含所有字符串
filtered_items = [item
for item in items
if all(target_str in str(item) for target_str in target_strings)
]
return filtered_items
@staticmethod
def clear_folder(target_dir: str|Path, max_attempts: int = 3, delay_seconds: float = 3) -> bool|None:
"""
通過刪除並重建文件夾的方式來清空目標文件夾,支持重試機制
Args:
max_attempts: 最大嘗試次數,默認為3次
delay_seconds: 每次重試前的等待時間(秒),默認為3秒
Returns:
bool: 成功清空返回 True,失敗返回 False
Raises:
PermissionError: 當文件被佔用且無法刪除時
OSError: 當發生其他操作系統錯誤時
"""
attempts = 0
while attempts < max_attempts:
try:
# 嘗試清空文件夾的核心邏輯
source_folder = Path(target_dir)
if source_folder.exists():
shutil.rmtree(source_folder) # 遞歸刪除整個文件夾
# 短暫延遲確保刪除完全生效
time.sleep(0.05)
source_folder.mkdir(exist_ok=True) # 重新創建空文件夾
print(f"成功清空文件夾: {source_folder}")
return True
except PermissionError as error:
# 捕獲權限錯誤(通常是因為文件被佔用)
attempts += 1
print(f"嘗試 {attempts}/{max_attempts} 失敗: 文件可能正被其他程序佔用。錯誤信息: {error}")
if attempts < max_attempts:
print(f"等待 {delay_seconds} 秒後重試...")
time.sleep(delay_seconds)
else:
print(f"已達到最大重試次數 {max_attempts},無法清空文件夾。")
return False
except OSError as error:
# 捕獲其他操作系統錯誤,例如路徑無效等
print(f"發生操作系統錯誤: {error}")
return False
def copy_directory_structure(self, recursive: bool = True) -> None:
"""
複製源目錄結構到目標位置(不復制文件內容)
遞歸複製源目錄下的所有子目錄結構到目標位置,但不會複製任何文件內容,
只創建空的目錄結構。
Args:
target_dir: 目標目錄路徑,可以是字符串或 Path 對象
"""
source_path = self.source_folder
target_path = self.target_folder
# 確保目標根目錄存在
target_path.mkdir(parents=True, exist_ok=True)
# 使用 rglob 遞歸遍歷源目錄下的所有子目錄
for dir_path in (source_path.rglob('*') if recursive else source_path.glob('*')):
if dir_path.is_dir():
# 計算當前目錄相對於源目錄的相對路徑
try:
relative_path = dir_path.relative_to(source_path)
except ValueError:
# 如果 dir_path 不是 source_path 的子路徑,跳過
continue
# 構建目標目錄路徑並創建
target_subdir = target_path / relative_path
target_subdir.mkdir(parents=True, exist_ok=True)
print(f"創建目錄: {target_subdir}")
def find_leaf_directories(self) -> list[Path]:
"""
查找並返回所有葉子目錄(不包含子目錄的目錄)
Returns:
葉子目錄的 Path 對象列表
"""
source_path = self.source_folder
leaf_directories = []
# 識別葉子目錄:沒有子目錄的目錄就是葉子目錄
for dir_path in source_path.rglob('*'):
if dir_path.is_dir():
# 檢查當前目錄是否包含子目錄
has_subdirectories = any(item.is_dir() for item in dir_path.iterdir())
if not has_subdirectories:
leaf_directories.append(dir_path)
# 輸出所有葉子目錄信息
result_paths = []
print("\n所有葉子目錄(最深層次的子目錄):")
for leaf_path in leaf_directories:
# 計算相對於源目錄的路徑用於顯示
try:
relative_leaf_path = leaf_path.relative_to(source_path)
result_paths.append(source_path / relative_leaf_path)
# 計算目錄深度
depth = len(relative_leaf_path.parts)
print(f"- {relative_leaf_path} (深度: {depth})")
except ValueError:
# 如果路徑計算出錯,跳過
continue
return result_paths
#將一個文件夾裏的所有指定格式文件複製到另一個目標文件夾
def copy_files(self, file_extension='.tif') -> None:
source_files = self.list_file_paths(True) #獲取原始文件夾裏所有的文件路徑
for source_file in source_files:
if file_extension in str(source_file):
shutil.copy2(source_file, self.target_folder)#將原始文件夾裏所有的文件複製到對應文件夾
print(f"已複製: {source_file} -> {target_folder}")
#將一個文件夾裏的所有目錄和指定格式文件複製到另一個文件夾
def copy_folders_and_files(self, file_extension='.tif') -> None:
self.copy_directory_structure(True)
source_files = self.list_file_paths(True) # 獲取原始文件夾裏所有的文件路徑
for source_file in source_files:
if file_extension in str(source_file):
# 計算 source_file 相對於 self.source_folder 的相對路徑
relative_path = source_file.relative_to(self.source_folder)
# 將相對路徑拼接到目標文件夾下
target_file = self.target_folder / relative_path
shutil.copy2(source_file, target_file) # 將原始文件夾裏所有的文件複製到對應文件夾
print(f"已複製: {source_file} -> {target_file}")
#將一個文件複製到目標文件夾裏,並重命名
@staticmethod
def copy_rename_file(source_file_path:str|Path, target_folder:str|Path,new_filename:str) -> None:
extension_name = Path(source_file_path).suffix
target_file_path = Path(target_folder) / Path(new_filename +extension_name)
shutil.copy2(source_file_path, target_file_path)
print(f"已複製: {source_file_path} -> {target_file_path}")
if __name__ == '__main__':
# # 測試路徑,請替換為實際路徑
source_folder = Path(r"F:\source")
target_folder = Path(r"F:\target")
file_io = FileIO(source_folder, target_folder) # 實例化類
target_strings = [".tif", ".docx"]
#
# 測試直接子文件夾相關方法
print("=== 直接子文件夾 ===")
current_folder_names = file_io.list_folder_names(False) #獲取直接文件夾名
current_folder_paths = file_io.list_folder_paths(False) #獲取直接文件夾路徑
filtered_current_folder_paths_and = file_io.filter_by_strings(current_folder_paths, target_strings, False) #按照邏輯與篩選含所有關鍵字的直接子文件夾路徑
filtered_current_folder_paths_or = file_io.filter_by_strings(current_folder_paths, target_strings, True) #按照邏輯或篩選含任一關鍵字的直接子文件夾路徑
print("直接子文件夾名稱列表:", current_folder_names)
print("直接子文件夾路徑列表:", current_folder_paths)
print("AND條件過濾的文件夾:", filtered_current_folder_paths_and)
print("OR條件過濾的文件夾:", filtered_current_folder_paths_or)
# 測試所有子文件夾相關方法
print("\n=== 所有子文件夾 ===")
all_folder_names = file_io.list_folder_names(True) #獲取所有子文件夾名
all_folder_paths = file_io.list_folder_paths(True) #獲取所有子文件夾路徑
filtered_all_folder_paths_and = file_io.filter_by_strings(all_folder_paths, target_strings, False) #按照邏輯與篩選含所有關鍵字的子文件夾路徑
filtered_all_folder_paths_or = file_io.filter_by_strings(all_folder_paths, target_strings, True) #按照邏輯或篩選含任一關鍵字的子文件夾路徑
print("所有子文件夾名稱:", all_folder_names)
print("所有子文件夾路徑:", all_folder_paths)
print("AND條件過濾的所有子文件夾:", filtered_all_folder_paths_and)
print("OR條件過濾的所有子文件夾:", filtered_all_folder_paths_or )
# 測試直接子文件相關方法
print("\n=== 直接子文件 ===")
current_file_lists_with_extension = file_io.list_file_names(False,True) #獲取所有直接文件的名稱,有擴展名
current_file_lists_no_extension = file_io.list_file_names(False,False) #獲取所有直接文件的名稱,無擴展名
current_file_path_lists = file_io.list_file_paths(False) #獲取直接文件路徑
filtered_file_path_lists_and = file_io.filter_by_strings(current_file_path_lists, target_strings, False) #按照邏輯與篩選含所有關鍵字的直接子文件路徑
filtered_file_path_lists_or = file_io.filter_by_strings(current_file_path_lists, target_strings, True) #按照邏輯或篩選含任一關鍵字的直接子文件路徑
print("帶擴展名的文件:", current_file_lists_with_extension)
print("不帶擴展名的文件:", current_file_lists_no_extension)
print("文件路徑列表:", current_file_path_lists )
print("AND條件過濾的文件:", filtered_file_path_lists_and)
print("OR條件過濾的文件:", filtered_file_path_lists_or)
# 測試所有子文件相關方法
print("\n=== 所有子文件 ===")
all_file_lists_with_extension = file_io.list_file_names(True,True) #獲取所有文件的名稱,有擴展名
all_file_lists_no_extension = file_io.list_file_names(True,False) #獲取所有文件的名稱,無擴展名
all_file_path_lists = file_io.list_file_paths(True) #獲取所有文件路徑
filtered_all_file_path_lists_and = file_io.filter_by_strings(all_file_path_lists, target_strings, False) #按照邏輯與篩選含所有關鍵字的所有文件路徑
filtered_all_file_path_lists_or = file_io.filter_by_strings(all_file_path_lists, target_strings, True) #按照邏輯或篩選含任一關鍵字的所有文件路徑
print("所有文件(帶擴展名):", all_file_lists_with_extension)
print("所有文件(不帶擴展名):", all_file_lists_no_extension)
print("所有文件路徑:", all_file_path_lists)
print("AND條件過濾的所有文件:", filtered_all_file_path_lists_and)
print("OR條件過濾的所有文件:", filtered_all_file_path_lists_or)
#清空目標文件夾裏的所有內容
file_io.clear_folder(file_io.target_folder)
#複製所有目錄
file_io.copy_directory_structure(True) #True,複製所有目錄,False,複製當前目錄
#找到所有葉子目錄
all_leaves = file_io.find_leaf_directories()
print(all_leaves)
# 清空目標文件夾裏的所有內容
file_io.clear_folder(file_io.target_folder)
#複製特定格式的文件到指定目錄
file_io.copy_files(".docx")
# 清空目標文件夾裏的所有內容
file_io.clear_folder(file_io.target_folder)
#複製並重命名
i=0
for filtered_all_file_path_lists_or in filtered_all_file_path_lists_or:
file_io.copy_rename_file(filtered_all_file_path_lists_or,file_io.target_folder,str(i))
i = i + 1
5 pathlib 與傳統 os.path 的對比
pathlib 相比傳統 os.path 操作有明顯優勢:
|
操作
|
os.path (傳統方式)
|
pathlib (現代方式)
|
|
路徑拼接
|
|
|
|
獲取文件名
|
|
|
|
獲取父目錄
|
|
|
|
檢查存在
|
|
|
|
判斷文件
|
|
|
|
判斷目錄
|
|
|
pathlib 的面向對象方式使代碼更簡潔、更易讀,而且自動處理操作系統差異,提高代碼的可移植性。
6 總結
pathlib 庫提供了一種現代化、面向對象的方法來處理文件系統路徑,比傳統的 os.path 模塊更直觀和強大。通過 Path 對象,我們可以用更簡潔的語法完成複雜的路徑操作,同時自動處理不同操作系統的路徑差異。
關鍵優勢包括:
- 直觀的路徑拼接操作(使用
/運算符) - 豐富的路徑解析方法(name、stem、suffix、parent 等)
- 便捷的文件系統操作(讀寫文件、目錄遍歷等)
- 更好的跨平台兼容性
- 更 Pythonic 的 API 設計
對於新項目,建議直接使用 pathlib 處理所有文件路徑操作,這將使代碼更簡潔、更易維護。