網站首頁 編程語言 正文
清理重復的文件
已知條件:
什么都不知道,只需要知道它是文件就可以了
實現方法:
可以從指定路徑(或最上層路徑)開始讀取,利用 glob 讀取每個文件夾,讀到文件,記錄名稱和大小,每一次檢測之前是否讀取過相同名稱的文件,如果存在,判斷大小是否相同,如果相同,我們就認為這是重復文件,將其刪除。
代碼示例如下:
# coding:utf-8
import glob
import os.path
data = {} # 定義一個空的字典,暫時將文件名存進來
def clear(path):
result = glob.glob(path) # 將 path 路徑傳入,賦值給 result
for _data in result: # for 循環判斷是否是文件夾
if glob.os.path.isdir(_data): # 若是文件夾,繼續將該文件夾的路徑傳給 clear() 函數繼續遞歸查找
_path = glob.os.path.join(_data, '*')
clear(_path)
else: # 若是文件,則將文件名提取出來
name = glob.os.path.split(_data)[-1]
if 'zip' in name: # 因為目前我們測試的 path 下有 ".zip" 文件,所以這里跳過 '.zip' 壓縮文件的讀取否則會報錯
continue
f = open(_data, 'r') # 判斷文件名之前先將內容讀取出來,若是不可讀模式
content = f.read() # 將讀取內容賦值給 content
if name in data: # 判斷文件名是否已存在 data 這個臨時存儲文件名的字典內,如果存在則進行執行刪除動作
_content_dict = data[name]
if _content_dict == content:
print('文件 \"{}\" 被刪除...'.format(_data)) # 調試
os.remove(_data)
else:
data[name] = content
if __name__ == '__main__':
path = glob.os.path.join(glob.os.getcwd(), 'test_file')
clear(path)
PS:這里需要注意一下,如果path的路徑是根目錄的話,會出現超級意外的結果,建議還是建立一個單獨的文件夾路徑來測試吧。(這個坑踩的我很心痛....)
運行結果如下:
清理重復文件的優化1
解決不同路徑下相同文件名不同內容問題
其實在這里大家能夠想到一個問題,可能會存在這樣一種情況,在不同的文件夾下存在著相同文件名,但是文件的內容卻是不相同的。如果利用上面的腳本執行針對文件夾下相同文件名的文件進行刪除的話,其實是一種不嚴謹的操作。
由此也就引出了我們接下來針對上文腳本優化的需求。
這里我們先看一下實際情況的 data 的值應該是怎樣的:data = {'name': {'path/name': 'content', 'path2/name': 'content'}}
- 上行內容的 name 為我們傳入路徑的根路徑
- 上行內容的 path/name 實際上為二級路徑
- 上行內容的 content 為文件內容
通過這種二級路徑與內容刪除的重復文件才是一種比較合理的方式。
示例代碼如下:
# coding:utf-8
import glob
import os.path
data = {} # 定義一個空的字典,暫時將文件名存進來
def clear(path):
result = glob.glob(path) # 將 path 路徑傳入,賦值給 result
for _data in result: # for 循環判斷是否是文件夾
if glob.os.path.isdir(_data): # 若是文件夾,繼續將該文件夾的路徑傳給 clear() 函數繼續遞歸查找
_path = glob.os.path.join(_data, '*')
clear(_path)
else: # 若是文件,則將文件名提取出來
name = glob.os.path.split(_data)[-1]
if 'zip' in name: # 因為目前我們測試的 path 下有 ".zip" 文件,所以這里跳過 '.zip' 壓縮文件的讀取否則會報錯
continue
f = open(_data, 'r') # 判斷文件名之前先將內容讀取出來,若是不可讀模式
content = f.read() # 將讀取內容賦值給 content
if name in data: # 判斷文件名是否已存在 data 這個臨時存儲文件名的字典內,如果存在則進行執行刪除動作
# 如果不存在,則將讀取到的二級路徑與內容存儲至 data 這個空字典內
sub_name = data[name] # 定義 sub_name 用以獲取二級路徑
is_delete = False # is_delete 用以記錄刪除狀態;如果沒有刪除,還需要將二級路徑添加至 data
for k, v in sub_name.items(): # 再次循環判斷二級路徑下的文件;k 為路徑,v 為文件內容
print('二級路徑為 \"{}\" ,'.format(k), name, '內容為 \'{}\' '.format(v)) # 調試打印輸出二級路徑下文件的循環
if v == content: # 如果文件名與內容相同,則執行刪除動作
print('文件 \"{}\" 被刪除...'.format(_data)) # 調試被刪除的文件
os.remove(_data) # 刪除重復文件后,變更 is_delete 狀態為True
is_delete = True
if not is_delete: # 如果沒有刪除則將 content 讀取到的內容賦值給 data[name][_data]
data[name][_data] = content
else:
data[name] = {
_data: content
}
if __name__ == '__main__':
path = glob.os.path.join(glob.os.getcwd(), 'test_file')
clear(path)
print(data)
運行結果如下:
清理重復文件的優化2
利用 hashlib模塊解決讀取文件過大問題
現在還有一個問題,從調試的打印輸出內容可以看出,因為用以測試的 test_file 路徑下的文件都比較小,所以運行起來沒有太大的問題;試想一下,如果是一些比較大的文件,經過讀取并存入字典的時候,就極大可能會造成內存不足等情況,所以這樣直接存儲內容的方法是顯然不合適的。
其實也是可以解決這個問題的, 就是利用到之前學習的加密模塊 ,通過 hashlib 模塊將內容加密成md5 的形式, md5只是一個很短的字符串,只要原始內容不變, md5 的值就不會變(該方法也經常被用于運維環境的文件安全檢測)。
所以代碼中的讀取文件內容的 content 就需要變更一下了:
代碼示例如下:
# coding:utf-8
import glob
import hashlib
import os.path
data = {} # 定義一個空的字典,暫時將文件名存進來
#data = {'name': {'path/name': 'content', 'path2/name': 'content'}}
def clear(path):
result = glob.glob(path) # 將 path 路徑傳入,賦值給 result
for _data in result: # for 循環判斷是否是文件夾
if glob.os.path.isdir(_data): # 若是文件夾,繼續將該文件夾的路徑傳給 clear() 函數繼續遞歸查找
_path = glob.os.path.join(_data, '*')
clear(_path)
else: # 若是文件,則將文件名提取出來
name = glob.os.path.split(_data)[-1]
if 'zip' in name: # 因為目前我們測試的 path 下有 ".zip" 文件,所以這里跳過 '.zip' 壓縮文件的讀取否則會報錯
continue
f = open(_data, 'r') # 判斷文件名之前先將內容讀取出來,若是不可讀模式
content = f.read() # 將讀取內容賦值給 content
hash_content_obj = hashlib.md5(content.encode('utf-8')) # 將讀取到的文件內容通過 md5 加密形式進行實例化
hash_content = hash_content_obj.hexdigest() # hash_content_obj 16進制字符串賦值給 hash_content
# 到這里,其實 data 存儲的就是 hash_content
if name in data: # 判斷文件名是否已存在 data 這個臨時存儲文件名的字典內,如果存在則進行執行刪除動作
# 如果不存在,則將讀取到的二級路徑與內容存儲至 data 這個空字典內
sub_name = data[name] # 定義 sub_name 用以獲取二級路徑
is_delete = False # is_delete 用以記錄刪除狀態;如果沒有刪除,還需要將二級路徑添加至 data
for k, v in sub_name.items(): # 再次循環判斷二級路徑下的文件;k 為路徑,v 為文件內容
print('二級路徑為 \"{}\" ,'.format(k), name, '內容為 \'{}\' '.format(v)) # 調試打印輸出二級路徑下文件的循環
if v == hash_content: # 如果文件名與內容相同,則執行刪除動作
print('文件 \"{}\" 被刪除...'.format(_data)) # 調試被刪除的文件
os.remove(_data) # 刪除重復文件后,變更 is_delete 狀態為True
is_delete = True
if not is_delete: # 如果沒有刪除則將 content 讀取到的內容賦值給 data[name][_data]
data[name][_data] = hash_content
else:
data[name] = {
_data: hash_content
}
if __name__ == '__main__':
path = glob.os.path.join(glob.os.getcwd(), 'test_file')
clear(path)
print(data)
運行結果如下:
清理重復文件的優化3
解決讀取到不可讀的 “zip” 文件報錯問題
在上文中,當我們遇到讀取到不可讀的 “zip” 壓縮文件時,利用的是 continue 的方式跳過。 其實這里說的 “zip” 不可讀取其實不太嚴謹,因為可以采用二進制讀取的方式來進行讀取,但是在上文腳本中,針對讀取的內容已經進行了 “encode” 編碼,所以用 rb 這種二進制讀取的方式還需要繼續進行優化。
示例代碼如下:
# coding:utf-8
import glob
import hashlib
#data = {'name': {'path/name': 'content', 'path2/name': 'content'}}
import os.path
data = {} # 定義一個空的字典,暫時將文件名存進來
def clear(path):
result = glob.glob(path) # 將 path 路徑傳入,賦值給 result
for _data in result: # for 循環判斷是否是文件夾
if glob.os.path.isdir(_data): # 若是文件夾,繼續將該文件夾的路徑傳給 clear() 函數繼續遞歸查找
_path = glob.os.path.join(_data, '*')
clear(_path)
else: # 若是文件,則將文件名提取出來
name = glob.os.path.split(_data)[-1]
is_byte = False # 添加一個 byte類型讀取的開關(如果文件中有中文,還需要設置一下編碼格式,并且將打開的文件關閉)
if 'zip' in name: # 因為目前我們測試的 path 下有 ".zip" 文件,所以這里跳過 '.zip' 壓縮文件的讀取否則會報錯
is_byte = True
f = open(_data, 'rb')
else:
f = open(_data, 'r', encoding='utf-8') # 判斷文件名之前先將內容讀取出來,若是不可讀模式
content = f.read() # 將讀取內容賦值給 content
f.close()
if is_byte:
hash_content_obj = hashlib.md5(content) # 將讀取到的文件內容通過 md5 加密形式進行實例化
else:
hash_content_obj = hashlib.md5(content.encode('utf-8'))
hash_content = hash_content_obj.hexdigest() # hash_content_obj 16進制字符串賦值給 hash_content
# 到這里,其實 data 存儲的就是 hash_content
if name in data: # 判斷文件名是否已存在 data 這個臨時存儲文件名的字典內,如果存在則進行執行刪除動作
# 如果不存在,則將讀取到的二級路徑與內容存儲至 data 這個空字典內
sub_name = data[name] # 定義 sub_name 用以獲取二級路徑
is_delete = False # is_delete 用以記錄刪除狀態;如果沒有刪除,還需要將二級路徑添加至 data
for k, v in sub_name.items(): # 再次循環判斷二級路徑下的文件;k 為路徑,v 為文件內容
print('二級路徑為 \"{}\" ,'.format(k), name, '內容為 \'{}\' '.format(v)) # 調試打印輸出二級路徑下文件的循環
if v == hash_content: # 如果文件名與內容相同,則執行刪除動作
print('文件 \"{}\" 被刪除...'.format(_data)) # 調試被刪除的文件
os.remove(_data) # 刪除重復文件后,變更 is_delete 狀態為True
is_delete = True
if not is_delete: # 如果沒有刪除則將 content 讀取到的內容賦值給 data[name][_data]
data[name][_data] = hash_content
else:
data[name] = {
_data: hash_content
}
if __name__ == '__main__':
path = glob.os.path.join(glob.os.getcwd(), 'test_file')
clear(path)
for k, v in data.items():
for _k, v in v.items():
print('文件路徑為 \"{}\" ,'.format(_k), '內容為 \'{}\' '.format(v))
運行結果如下:
批量修改文件名
其實也很簡單,依然是使用我們最近學習的 shutil 與 glob 模塊(參考上一章節的文件查找與遞歸實現的方式)。
已知條件:
知道文件名需要被修改的指定字符串(即需要被修改的文件名)
實現方法:
通過循環,將指定的目標字符串加入或修改文件名稱中含有的字符串
代碼示例如下:
# coding:utf-8
import glob
import os.path
import shutil
'''
利用for循環及遞歸這樣的方式,通過 glob 去讀取到 test_file 下所有的內容
通過循環按照循環的每一個索引,把每一個文件添加上索引標
'''
def filename_update(path):
result = glob.glob(path)
for index, data in enumerate(result): # for 循環枚舉:如果是文件夾則進行遞歸,如果是文件則加上索引值
if glob.os.path.isdir(data):
_path = glob.os.path.join(data, '*')
filename_update(_path)
else:
path_list = glob.os.path.split(data)
name = path_list[-1]
new_name = '{}_{}'.format(index, name)
new_data = glob.os.path.join(path_list[0], new_name)
shutil.move(data, new_data)
if __name__ == '__main__':
path = glob.os.path.join(glob.os.getcwd(), 'test_file')
filename_update(path)
運行結果如下:
可能這里大家有注意到 "test_file" 文件加下沒有 "0_*"開頭的索引,其實并不是沒有索引,而是我們的腳本中只重命名了文件,文件夾被過濾掉了。
原文鏈接:https://blog.csdn.net/weixin_42250835/article/details/124678021
相關推薦
- 2022-07-29 cypress測試本地web應用_web2.0
- 2022-05-31 Python學習之日志模塊詳解_python
- 2022-01-30 uniapp H5刷新404問題解決 apache配置
- 2022-09-18 python實現修改xml文件內容_python
- 2022-07-19 詳解c語言中的動態內存分配問題
- 2023-08-16 數據選擇器 uni-data-checkbox,獲取value值
- 2022-02-23 利用?trap?在?docker?容器優雅關閉前執行環境清理的方案_docker
- 2022-05-08 繼docker之后podman容器技術崛起_docker
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支