刪除目錄下相同檔案的python代碼(逐級最佳化)

來源:互聯網
上載者:User
這兩天閑來無事在百度上淘了點圖片,不多,也就幾萬張吧,其中有不少美女圖片奧!哈哈!這裡暫且不說圖片是怎麼獲得的,咱聊聊得到圖片以後發生的事。
遇到的第一個問題就是有些圖片沒有尾碼名。在windows下,沒有尾碼名的檔案是不能正確被識別的,沒有預覽,開啟時還要選擇開啟檔案,費勁!這個問題比較容易解決,給每個圖片加上尾碼名就是了。沒有尾碼名的圖片也不多,不到1000張吧,一張一張地改很麻煩,還好我是學電腦的,上午寫了個程式批量修改http://www.bitsCN.com/article/30400.htm。這個問題就算解決了。接下來又遇到了一個新問題:圖片多了,難免出現重複的,有些圖片完全一樣,沒有必要都留著,我就想把所有的重複圖片都刪除。

讓我們來分析一下這個問題:首先,檔案個數非常多,手工尋找是不現實的,再說,單憑我們肉眼,在幾千張圖片裡面找到完全相同的難度也是很大的。如果不是圖片而是其他文檔,在不能預覽的情況下要正確區分是很困難的。所以要用程式實現。那麼用程式怎麼實現呢?根據什麼判斷兩個檔案完全相同呢?首先,根據檔案名稱判斷是靠不住的,因為檔案名稱可以被隨意更改,但檔案內容不變。再說在同一個檔案夾下面,也不可能出現兩個完全相同的檔案名稱,作業系統不允許的。還有一種方法就是根據檔案大小來判斷,這不失為一種好辦法,但是,檔案大小相同的圖片可能不一樣。再說圖片一般都比較小,超過3M的基本沒有,大部分不夠1M,如果檔案夾下面檔案特別多,出現大小相同的的檔案可能性是相當大的。所以單憑檔案大小來比較不靠譜。還有一種方法是讀取每張圖片的內容,然後比較這個圖片的內容和其他圖片是否完全相同,如果內容相同那麼這兩張圖片肯定是完全相同的。這種方法看起來是比較完美的,讓我們來分析一下他的時空效率:首先每張圖片的內容都要和其他圖片進行比較,這就是一個二重迴圈,讀取的效率低,比較的效率更低,所有的都比較下來是非常費時的!記憶體方面,如果預先把所有圖片讀取到記憶體可以加快檔案的比較效率,但是普通電腦的記憶體資源有限,如果圖片非常多,好幾個G的話,都讀到記憶體是不現實的。如果不把所有的檔案讀取到記憶體,那麼每比較一次之前就要先讀取檔案內容,比較幾次就要讀取幾次,從硬碟讀取資料是比較慢的,這樣做顯然不合適。那麼有沒有更好的方法呢?我冥思苦想,絞盡腦汁,最後想到了md5。md5是什嗎?你不知道嗎?額,你火星了,抓緊時間duckduckgo吧!也許你會問,md5不是加密的嗎?和我們的問題有關係嗎?問得好!md5可以把任意長度的字串進行加密後形成一個32的字元序列,包括數字和字母(大寫或小寫),因為字串任何微小的變動都會導致md5序列改變,因此md5可以看作一個字串的‘指紋'或者‘資訊摘要',因為md5字串總共有3632個,所以兩個不同的字串得到一個相同的md5機率是很小的,幾乎為0,同樣的道理,我們可以得到每個檔案的md5,若干檔案的md5相同的話就基本上可以肯定兩個檔案是相同的,因為md5相同而檔案不同的機率太小了,基本可以忽略,這樣我們就可以這樣做:得到每個檔案的md5,通過比較md5是否相同我們就可以確定兩張圖片是否相同。下面是代碼實現,python的

代碼如下:


# -*- coding: cp936 -*-
import md5
import os
from time import clock as now
def getmd5(filename):
file_txt = open(filename,'rb').read()
m = md5.new(file_txt)
return m.hexdigest()
def main():
path = raw_input("path: ")
all_md5=[]
total_file=0
total_delete=0
start=now()
for file in os.listdir(path):
total_file += 1;
real_path=os.path.join(path,file)
if os.path.isfile(real_path) == True:
filemd5=getmd5(real_path)
if filemd5 in all_md5:
total_delete += 1
print '刪除',file
else:
all_md5.append(filemd5)
end = now()
time_last = end - start
print '檔案總數:',total_file
print '刪除個數:',total_delete
print '耗時:',time_last,'秒'
if __name__=='__main__':
main()


上面的程式原理很簡單,就是依次讀取每個檔案,計算md5,如果md5在md5列表不存在,就把這個md5加到md5列表裡面去,如果存在的話,我們就認為這個md5對應的檔案已經出現過,這個圖片就是多餘的,然後我們就可以把這個圖片刪除了。下面是程式的運行:

我們可以看到,在這個檔案夾下面有8674個檔案,有31個是重複的,找到所有重複檔案共耗時155.5秒。效率不算高,能不能進行最佳化呢?我分析了一下,我的程式裡面有兩個功能比較耗時間,一個是計算每個檔案的md5,這個佔了大部分時間,還有就是在列表中尋找md5是否存在,也比較費時間的。從這兩方面入手,我們可以進一步最佳化。

首先我想的是解決尋找問題,或許我們可以對列表中的元素先排一下序,然後再去尋找,但是列表是變化的,每次都排序的話效率就比較低了。我想的是利用字典進行最佳化。字典最顯著的特點是一個key對應一個值我們可以把md5作為key,key對應的值就不需要了,在變化的情況下字典的尋找效率比序列效率高,因為序列是無序的,而字典是有序的,尋找起來當然更快。這樣我們只要判斷md5值是否在所有的key中就可以了。下面是改進後的代碼:

代碼如下:


# -*- coding: cp936 -*-
import md5
import os
from time import clock as now
def getmd5(filename):
file_txt = open(filename,'rb').read()
m = md5.new(file_txt)
return m.hexdigest()
def main():
path = raw_input("path: ")
all_md5={}
total_file=0
total_delete=0
start=now()
for file in os.listdir(path):
total_file += 1;
real_path=os.path.join(path,file)
if os.path.isfile(real_path) == True:
filemd5=getmd5(real_path)
if filemd5 in all_md5.keys():
total_delete += 1
print '刪除',file
else:
all_md5[filemd5]=''
end = now()
time_last = end - start
print '檔案總數:',total_file
print '刪除個數:',total_delete
print '耗時:',time_last,'秒'

if __name__=='__main__':
main()



從時間上看,確實比原來快了一點,但是還不理想。下面還要進行最佳化。還有什麼可以最佳化呢?md5!上面的程式,每個檔案都要計算md5,非常費時間,是不是每個檔案都需要計算md5呢?能不能想辦法減少md5的計算次數呢?我想到了一種方法:上面分析時我們提到,可以通過比較檔案大小的方式來判斷圖片是否完全相同,速度快,但是這種方法是不準確的,md5是準確的,我們能不能把兩者結合一下?答案是肯定的。我們可以認定:如果兩個檔案完全相同,那麼這兩個檔案的大小和md5一定相同,如果兩個檔案的大小不同,那麼這兩個檔案肯定不同!這樣的話,我們只需要先查看檔案的大小是否存在在size字典中,如果不存在,就將它加入到size字典中,如果大小存在的話,這說明有至少兩張圖片大小相同,那麼我們只要計算檔案大小相同的檔案的md5,如果md5相同,那麼這兩個檔案肯定完全一樣,我們可以刪除,如果md5不同,我們把它加到列表裡面,避免重複計算md5.具體代碼實現如下:

代碼如下:


# -*- coding: cp936 -*-
import md5
import os
from time import clock as now
def getmd5(filename):
file_txt = open(filename,'rb').read()
m = md5.new(file_txt)
return m.hexdigest()
def main():
path = raw_input("path: ")
all_md5 = {}
all_size = {}
total_file=0
total_delete=0
start=now()
for file in os.listdir(path):
total_file += 1
real_path=os.path.join(path,file)
if os.path.isfile(real_path) == True:
size = os.stat(real_path).st_size
name_and_md5=[real_path,'']
if size in all_size.keys():
new_md5 = getmd5(real_path)
if all_size[size][1]=='':
all_size[size][1]=getmd5(all_size[size][0])
if new_md5 in all_size[size]:
print '刪除',file
total_delete += 1
else:
all_size[size].append(new_md5)
else:
all_size[size]=name_and_md5
end = now()
time_last = end - start
print '檔案總數:',total_file
print '刪除個數:',total_delete
print '耗時:',time_last,'秒'

if __name__=='__main__':
main()


時間效率怎樣呢?看:

只用了7.28秒!比前兩個效率提高了十幾倍!這個時間還可以接受

演算法是個很神奇的東西,不經意間用一下會有意想不到的收穫!上面的代碼還可以進一步最佳化,比如改進尋找演算法等,讀者有啥想法可以和我交流一下。換成C語言來實現可能會更快。呵呵,我喜歡python的簡潔!
博主ma6174

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.