Python自動(dòng)化辦公之清理重復(fù)文件詳解
清理重復(fù)的文件
已知條件:
什么都不知道,只需要知道它是文件就可以了
實(shí)現(xiàn)方法:
可以從指定路徑(或最上層路徑)開(kāi)始讀取,利用 glob 讀取每個(gè)文件夾,讀到文件,記錄名稱和大小,每一次檢測(cè)之前是否讀取過(guò)相同名稱的文件,如果存在,判斷大小是否相同,如果相同,我們就認(rèn)為這是重復(fù)文件,將其刪除。
代碼示例如下:
# coding:utf-8 import glob import os.path data = {} # 定義一個(gè)空的字典,暫時(shí)將文件名存進(jìn)來(lái) def clear(path): result = glob.glob(path) # 將 path 路徑傳入,賦值給 result for _data in result: # for 循環(huán)判斷是否是文件夾 if glob.os.path.isdir(_data): # 若是文件夾,繼續(xù)將該文件夾的路徑傳給 clear() 函數(shù)繼續(xù)遞歸查找 _path = glob.os.path.join(_data, '*') clear(_path) else: # 若是文件,則將文件名提取出來(lái) name = glob.os.path.split(_data)[-1] if 'zip' in name: # 因?yàn)槟壳拔覀儨y(cè)試的 path 下有 ".zip" 文件,所以這里跳過(guò) '.zip' 壓縮文件的讀取否則會(huì)報(bào)錯(cuò) continue f = open(_data, 'r') # 判斷文件名之前先將內(nèi)容讀取出來(lái),若是不可讀模式 content = f.read() # 將讀取內(nèi)容賦值給 content if name in data: # 判斷文件名是否已存在 data 這個(gè)臨時(shí)存儲(chǔ)文件名的字典內(nèi),如果存在則進(jìn)行執(zhí)行刪除動(dòng)作 _content_dict = data[name] if _content_dict == content: print('文件 \"{}\" 被刪除...'.format(_data)) # 調(diào)試 os.remove(_data) else: data[name] = content if __name__ == '__main__': path = glob.os.path.join(glob.os.getcwd(), 'test_file') clear(path)
PS:這里需要注意一下,如果path的路徑是根目錄的話,會(huì)出現(xiàn)超級(jí)意外的結(jié)果,建議還是建立一個(gè)單獨(dú)的文件夾路徑來(lái)測(cè)試吧。(這個(gè)坑踩的我很心痛....)
運(yùn)行結(jié)果如下:
清理重復(fù)文件的優(yōu)化1
解決不同路徑下相同文件名不同內(nèi)容問(wèn)題
其實(shí)在這里大家能夠想到一個(gè)問(wèn)題,可能會(huì)存在這樣一種情況,在不同的文件夾下存在著相同文件名,但是文件的內(nèi)容卻是不相同的。如果利用上面的腳本執(zhí)行針對(duì)文件夾下相同文件名的文件進(jìn)行刪除的話,其實(shí)是一種不嚴(yán)謹(jǐn)?shù)牟僮鳌?/p>
由此也就引出了我們接下來(lái)針對(duì)上文腳本優(yōu)化的需求。
這里我們先看一下實(shí)際情況的 data 的值應(yīng)該是怎樣的:data = {'name': {'path/name': 'content', 'path2/name': 'content'}}
- 上行內(nèi)容的 name 為我們傳入路徑的根路徑
- 上行內(nèi)容的 path/name 實(shí)際上為二級(jí)路徑
- 上行內(nèi)容的 content 為文件內(nèi)容
通過(guò)這種二級(jí)路徑與內(nèi)容刪除的重復(fù)文件才是一種比較合理的方式。
示例代碼如下:
# coding:utf-8 import glob import os.path data = {} # 定義一個(gè)空的字典,暫時(shí)將文件名存進(jìn)來(lái) def clear(path): result = glob.glob(path) # 將 path 路徑傳入,賦值給 result for _data in result: # for 循環(huán)判斷是否是文件夾 if glob.os.path.isdir(_data): # 若是文件夾,繼續(xù)將該文件夾的路徑傳給 clear() 函數(shù)繼續(xù)遞歸查找 _path = glob.os.path.join(_data, '*') clear(_path) else: # 若是文件,則將文件名提取出來(lái) name = glob.os.path.split(_data)[-1] if 'zip' in name: # 因?yàn)槟壳拔覀儨y(cè)試的 path 下有 ".zip" 文件,所以這里跳過(guò) '.zip' 壓縮文件的讀取否則會(huì)報(bào)錯(cuò) continue f = open(_data, 'r') # 判斷文件名之前先將內(nèi)容讀取出來(lái),若是不可讀模式 content = f.read() # 將讀取內(nèi)容賦值給 content if name in data: # 判斷文件名是否已存在 data 這個(gè)臨時(shí)存儲(chǔ)文件名的字典內(nèi),如果存在則進(jìn)行執(zhí)行刪除動(dòng)作 # 如果不存在,則將讀取到的二級(jí)路徑與內(nèi)容存儲(chǔ)至 data 這個(gè)空字典內(nèi) sub_name = data[name] # 定義 sub_name 用以獲取二級(jí)路徑 is_delete = False # is_delete 用以記錄刪除狀態(tài);如果沒(méi)有刪除,還需要將二級(jí)路徑添加至 data for k, v in sub_name.items(): # 再次循環(huán)判斷二級(jí)路徑下的文件;k 為路徑,v 為文件內(nèi)容 print('二級(jí)路徑為 \"{}\" ,'.format(k), name, '內(nèi)容為 \'{}\' '.format(v)) # 調(diào)試打印輸出二級(jí)路徑下文件的循環(huán) if v == content: # 如果文件名與內(nèi)容相同,則執(zhí)行刪除動(dòng)作 print('文件 \"{}\" 被刪除...'.format(_data)) # 調(diào)試被刪除的文件 os.remove(_data) # 刪除重復(fù)文件后,變更 is_delete 狀態(tài)為True is_delete = True if not is_delete: # 如果沒(méi)有刪除則將 content 讀取到的內(nèi)容賦值給 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)
運(yùn)行結(jié)果如下:
清理重復(fù)文件的優(yōu)化2
利用 hashlib模塊解決讀取文件過(guò)大問(wèn)題
現(xiàn)在還有一個(gè)問(wèn)題,從調(diào)試的打印輸出內(nèi)容可以看出,因?yàn)橛靡詼y(cè)試的 test_file 路徑下的文件都比較小,所以運(yùn)行起來(lái)沒(méi)有太大的問(wèn)題;試想一下,如果是一些比較大的文件,經(jīng)過(guò)讀取并存入字典的時(shí)候,就極大可能會(huì)造成內(nèi)存不足等情況,所以這樣直接存儲(chǔ)內(nèi)容的方法是顯然不合適的。
其實(shí)也是可以解決這個(gè)問(wèn)題的, 就是利用到之前學(xué)習(xí)的加密模塊 ,通過(guò) hashlib 模塊將內(nèi)容加密成md5 的形式, md5只是一個(gè)很短的字符串,只要原始內(nèi)容不變, md5 的值就不會(huì)變(該方法也經(jīng)常被用于運(yùn)維環(huán)境的文件安全檢測(cè))。
所以代碼中的讀取文件內(nèi)容的 content 就需要變更一下了:
代碼示例如下:
# coding:utf-8 import glob import hashlib import os.path data = {} # 定義一個(gè)空的字典,暫時(shí)將文件名存進(jìn)來(lái) #data = {'name': {'path/name': 'content', 'path2/name': 'content'}} def clear(path): result = glob.glob(path) # 將 path 路徑傳入,賦值給 result for _data in result: # for 循環(huán)判斷是否是文件夾 if glob.os.path.isdir(_data): # 若是文件夾,繼續(xù)將該文件夾的路徑傳給 clear() 函數(shù)繼續(xù)遞歸查找 _path = glob.os.path.join(_data, '*') clear(_path) else: # 若是文件,則將文件名提取出來(lái) name = glob.os.path.split(_data)[-1] if 'zip' in name: # 因?yàn)槟壳拔覀儨y(cè)試的 path 下有 ".zip" 文件,所以這里跳過(guò) '.zip' 壓縮文件的讀取否則會(huì)報(bào)錯(cuò) continue f = open(_data, 'r') # 判斷文件名之前先將內(nèi)容讀取出來(lái),若是不可讀模式 content = f.read() # 將讀取內(nèi)容賦值給 content hash_content_obj = hashlib.md5(content.encode('utf-8')) # 將讀取到的文件內(nèi)容通過(guò) md5 加密形式進(jìn)行實(shí)例化 hash_content = hash_content_obj.hexdigest() # hash_content_obj 16進(jìn)制字符串賦值給 hash_content # 到這里,其實(shí) data 存儲(chǔ)的就是 hash_content if name in data: # 判斷文件名是否已存在 data 這個(gè)臨時(shí)存儲(chǔ)文件名的字典內(nèi),如果存在則進(jìn)行執(zhí)行刪除動(dòng)作 # 如果不存在,則將讀取到的二級(jí)路徑與內(nèi)容存儲(chǔ)至 data 這個(gè)空字典內(nèi) sub_name = data[name] # 定義 sub_name 用以獲取二級(jí)路徑 is_delete = False # is_delete 用以記錄刪除狀態(tài);如果沒(méi)有刪除,還需要將二級(jí)路徑添加至 data for k, v in sub_name.items(): # 再次循環(huán)判斷二級(jí)路徑下的文件;k 為路徑,v 為文件內(nèi)容 print('二級(jí)路徑為 \"{}\" ,'.format(k), name, '內(nèi)容為 \'{}\' '.format(v)) # 調(diào)試打印輸出二級(jí)路徑下文件的循環(huán) if v == hash_content: # 如果文件名與內(nèi)容相同,則執(zhí)行刪除動(dòng)作 print('文件 \"{}\" 被刪除...'.format(_data)) # 調(diào)試被刪除的文件 os.remove(_data) # 刪除重復(fù)文件后,變更 is_delete 狀態(tài)為True is_delete = True if not is_delete: # 如果沒(méi)有刪除則將 content 讀取到的內(nèi)容賦值給 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)
運(yùn)行結(jié)果如下:
清理重復(fù)文件的優(yōu)化3
解決讀取到不可讀的 “zip” 文件報(bào)錯(cuò)問(wèn)題
在上文中,當(dāng)我們遇到讀取到不可讀的 “zip” 壓縮文件時(shí),利用的是 continue 的方式跳過(guò)。 其實(shí)這里說(shuō)的 “zip” 不可讀取其實(shí)不太嚴(yán)謹(jǐn),因?yàn)榭梢圆捎枚M(jìn)制讀取的方式來(lái)進(jìn)行讀取,但是在上文腳本中,針對(duì)讀取的內(nèi)容已經(jīng)進(jìn)行了 “encode” 編碼,所以用 rb 這種二進(jìn)制讀取的方式還需要繼續(xù)進(jìn)行優(yōu)化。
示例代碼如下:
# coding:utf-8 import glob import hashlib #data = {'name': {'path/name': 'content', 'path2/name': 'content'}} import os.path data = {} # 定義一個(gè)空的字典,暫時(shí)將文件名存進(jìn)來(lái) def clear(path): result = glob.glob(path) # 將 path 路徑傳入,賦值給 result for _data in result: # for 循環(huán)判斷是否是文件夾 if glob.os.path.isdir(_data): # 若是文件夾,繼續(xù)將該文件夾的路徑傳給 clear() 函數(shù)繼續(xù)遞歸查找 _path = glob.os.path.join(_data, '*') clear(_path) else: # 若是文件,則將文件名提取出來(lái) name = glob.os.path.split(_data)[-1] is_byte = False # 添加一個(gè) byte類型讀取的開(kāi)關(guān)(如果文件中有中文,還需要設(shè)置一下編碼格式,并且將打開(kāi)的文件關(guān)閉) if 'zip' in name: # 因?yàn)槟壳拔覀儨y(cè)試的 path 下有 ".zip" 文件,所以這里跳過(guò) '.zip' 壓縮文件的讀取否則會(huì)報(bào)錯(cuò) is_byte = True f = open(_data, 'rb') else: f = open(_data, 'r', encoding='utf-8') # 判斷文件名之前先將內(nèi)容讀取出來(lái),若是不可讀模式 content = f.read() # 將讀取內(nèi)容賦值給 content f.close() if is_byte: hash_content_obj = hashlib.md5(content) # 將讀取到的文件內(nèi)容通過(guò) md5 加密形式進(jìn)行實(shí)例化 else: hash_content_obj = hashlib.md5(content.encode('utf-8')) hash_content = hash_content_obj.hexdigest() # hash_content_obj 16進(jìn)制字符串賦值給 hash_content # 到這里,其實(shí) data 存儲(chǔ)的就是 hash_content if name in data: # 判斷文件名是否已存在 data 這個(gè)臨時(shí)存儲(chǔ)文件名的字典內(nèi),如果存在則進(jìn)行執(zhí)行刪除動(dòng)作 # 如果不存在,則將讀取到的二級(jí)路徑與內(nèi)容存儲(chǔ)至 data 這個(gè)空字典內(nèi) sub_name = data[name] # 定義 sub_name 用以獲取二級(jí)路徑 is_delete = False # is_delete 用以記錄刪除狀態(tài);如果沒(méi)有刪除,還需要將二級(jí)路徑添加至 data for k, v in sub_name.items(): # 再次循環(huán)判斷二級(jí)路徑下的文件;k 為路徑,v 為文件內(nèi)容 print('二級(jí)路徑為 \"{}\" ,'.format(k), name, '內(nèi)容為 \'{}\' '.format(v)) # 調(diào)試打印輸出二級(jí)路徑下文件的循環(huán) if v == hash_content: # 如果文件名與內(nèi)容相同,則執(zhí)行刪除動(dòng)作 print('文件 \"{}\" 被刪除...'.format(_data)) # 調(diào)試被刪除的文件 os.remove(_data) # 刪除重復(fù)文件后,變更 is_delete 狀態(tài)為True is_delete = True if not is_delete: # 如果沒(méi)有刪除則將 content 讀取到的內(nèi)容賦值給 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), '內(nèi)容為 \'{}\' '.format(v))
運(yùn)行結(jié)果如下:
批量修改文件名
其實(shí)也很簡(jiǎn)單,依然是使用我們最近學(xué)習(xí)的 shutil 與 glob 模塊(參考上一章節(jié)的文件查找與遞歸實(shí)現(xiàn)的方式)。
已知條件:
知道文件名需要被修改的指定字符串(即需要被修改的文件名)
實(shí)現(xiàn)方法:
通過(guò)循環(huán),將指定的目標(biāo)字符串加入或修改文件名稱中含有的字符串
代碼示例如下:
# coding:utf-8 import glob import os.path import shutil ''' 利用for循環(huán)及遞歸這樣的方式,通過(guò) glob 去讀取到 test_file 下所有的內(nèi)容 通過(guò)循環(huán)按照循環(huán)的每一個(gè)索引,把每一個(gè)文件添加上索引標(biāo) ''' def filename_update(path): result = glob.glob(path) for index, data in enumerate(result): # for 循環(huán)枚舉:如果是文件夾則進(jìn)行遞歸,如果是文件則加上索引值 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)
運(yùn)行結(jié)果如下:
可能這里大家有注意到 "test_file" 文件加下沒(méi)有 "0_*"開(kāi)頭的索引,其實(shí)并不是沒(méi)有索引,而是我們的腳本中只重命名了文件,文件夾被過(guò)濾掉了。
到此這篇關(guān)于Python自動(dòng)化辦公之清理重復(fù)文件詳解的文章就介紹到這了,更多相關(guān)Python清理重復(fù)文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用Python實(shí)現(xiàn)端口掃描器的全過(guò)程
這篇文章主要給大家介紹了關(guān)于如何利用Python實(shí)現(xiàn)端口掃描器的相關(guān)資料,用來(lái)檢測(cè)目標(biāo)服務(wù)器上有哪些端口開(kāi)放,本文適用于有 Python和計(jì)算機(jī)網(wǎng)絡(luò)語(yǔ)言基礎(chǔ)的用戶,需要的朋友可以參考下2021-08-08Python生成隨機(jī)數(shù)組的方法小結(jié)
這篇文章主要介紹了Python生成隨機(jī)數(shù)組的方法,結(jié)合實(shí)例形式總結(jié)分析了Python使用random模塊生成隨機(jī)數(shù)與數(shù)組操作相關(guān)技巧,需要的朋友可以參考下2017-04-04對(duì)Python3中的print函數(shù)以及與python2的對(duì)比分析
下面小編就為大家分享一篇對(duì)Python3中的print函數(shù)以及與python2的對(duì)比分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05Python?torch.onnx.export用法詳細(xì)介紹
這篇文章主要給大家介紹了關(guān)于Python?torch.onnx.export用法詳細(xì)介紹的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-07-07