-- 系列:日常辦公技巧
-- 作者:alex tang
-- 時(shí)間:01/21-2019
## 一忆绰、業(yè)務(wù)背景
### 1.1背景簡(jiǎn)介
幾年的咨詢工作不僅讓我變得更有經(jīng)驗(yàn)怖竭,更養(yǎng)成了我松鼠黨的習(xí)慣。堆積的資料越來(lái)越多叉谜,而且重復(fù)的文件被放在了不同的目錄旗吁,占用的空間越來(lái)越大,數(shù)量也多得已經(jīng)不太適合人工分辨整理停局。尤其是有的一模一樣的文件被改成不一樣的名字很钓,但實(shí)質(zhì)相同。這種壞現(xiàn)象不僅是電腦董栽,連云盤也是如此码倦。
前兩天整理自己的網(wǎng)盤時(shí)發(fā)現(xiàn)空間已滿,想要使用使用其文件去重功能居然要開通會(huì)員锭碳,最低一檔需付費(fèi)30大洋袁稽。作為科技宅遂決定不讓自己的python吃灰了,自己寫著玩玩擒抛。
本文會(huì)和大家分享如何實(shí)現(xiàn)文件去重推汽,如何掃描自己硬盤和網(wǎng)盤的每一個(gè)目錄。
### 1.2知識(shí)點(diǎn)預(yù)告
本文完成過(guò)程中將使用:
* MD5原理
* 遍歷文件夾
* pathlib模塊的使用
* logging模塊的使用
### 1.3使用環(huán)境
python=3.6.7
pathlib=2.3.2
## 二歧沪、實(shí)現(xiàn)原理
在開始之前歹撒,我先解釋一下這些代碼運(yùn)行的原理——基于文件的完整性( integrity)。如果兩個(gè)文件的內(nèi)容相同诊胞,不管它們文件名相同與否暖夭,它們的MD5值(或者其他哈希算法值)必定是相同的。在這篇文章里撵孤,我使用MD5值來(lái)判斷文件的完整性迈着。
## 三、開發(fā)準(zhǔn)備
linux打開終端邪码,windows使用win+r打開cmd命令行寥假。進(jìn)入code目錄,創(chuàng)建work文件夾霞扬,將其作為工作目錄糕韧。
```python
! cd C:\test\Code&&mkdir work&&cd work
```
? ? 子目錄或文件 work 已經(jīng)存在枫振。
為了實(shí)驗(yàn),我在該文件夾下面放了一個(gè)文件萤彩,并復(fù)制了一下粪滤。這樣兩個(gè)文件的名字就不相同了。

安裝所需模塊
```python
! pip install hashlib
! pip install logging
! pip install pathlib
```
## 四雀扶、開發(fā)步驟
### 4.1導(dǎo)入所需package
```python
import os
import hashlib
import logging
import sys
from pathlib import Path
```
### 4.2編寫日志函數(shù)
指定logger輸出格式杖小,明確日志文件名稱和存儲(chǔ)位置,制定日志的最低輸出級(jí)別愚墓。
```python
def logger():
? ? """ 獲取logger"""
? ? logger = logging.getLogger()
? ? if not logger.handlers:
? ? ? ? # 指定logger輸出格式
? ? ? ? formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s')
? ? ? ? # 文件日志
? ? ? ? file_handler = logging.FileHandler("test.log")
? ? ? ? file_handler.setFormatter(formatter)? # 可以通過(guò)setFormatter指定輸出格式
? ? ? ? # 控制臺(tái)日志
? ? ? ? console_handler = logging.StreamHandler(sys.stdout)
? ? ? ? console_handler.formatter = formatter? # 也可以直接給formatter賦值
? ? ? ? # 為logger添加的日志處理器
? ? ? ? logger.addHandler(file_handler)
? ? ? ? logger.addHandler(console_handler)
? ? ? ? # 指定日志的最低輸出級(jí)別予权,默認(rèn)為WARN級(jí)別
? ? ? ? logger.setLevel(logging.INFO)
? ? return logger
```
### 4.3獲得文件的唯一標(biāo)識(shí) - MD5值
假如你要處理的重復(fù)文件有不同的文件名,最簡(jiǎn)單的辦法就是通過(guò)MD5來(lái)確定兩個(gè)文件是不是一樣的浪册。
```python
def md5sum(filename, blocksize=65536):
? ? hash = hashlib.md5()
? ? with open(filename, "rb") as f:
? ? ? ? for block in iter(lambda: f.read(blocksize), b""):
? ? ? ? ? ? hash.update(block)
? ? return hash.hexdigest()
```
這個(gè)方法可以快速獲得一個(gè)文件的MD5值扫腺,blocksize 可以根據(jù)文件大小和CPU性能調(diào)整,一般選擇的值約等于文件的平均大小村象。
### 4.4保存所有文件標(biāo)識(shí)和路徑
接下來(lái)遍歷所有文件笆环,使用MD5作為key,路徑作為value厚者,保存起來(lái)躁劣。
```python
dup = {}
def build_hash_dict(dir_path, pattern='*.xlsx'):
? ? def save(file):
? ? ? ? hash = md5sum(file)
? ? ? ? if hash not in dup.keys():
? ? ? ? ? ? dup[hash] = [file]
? ? ? ? else:
? ? ? ? ? ? dup[hash].append(file)
? ? p = Path(dir_path)
? ? for item in p.glob('**/' + pattern):
? ? ? ? save(str(item))
```
### 4.5處理重復(fù)文件
最后一步非常簡(jiǎn)單,把上一步建立的字典做一個(gè)簡(jiǎn)單的過(guò)濾就能找到重復(fù)文件库菲。
```python
def main():
? ? log = logger()
? ? def get_duplicate():
? ? ? ? return {k: v for k, v in dup.items() if len(v) > 1}
? ? build_hash_dict(work_path)
? ? for hash, files in get_duplicate().items():
? ? ? ? log.info("{}: {}".format(hash, files))
```
如果你想直接刪除冗余文件账忘,而不是自己來(lái)判斷從哪個(gè)文件里面刪除,可在記錄日志后遍歷日志文件列表熙宇,刪除指定文件之外的其他文件內(nèi)容鳖擒。
? ? ? ? #import os
? ? ? ? #[os.remove(file) for file in files[1:]]
### 4.6執(zhí)行函數(shù)代碼
```python
if __name__ == '__main__':
? ? dup = {}
? ? work_path = 'C:\\test\\Code\\work'
? ? main()
```
? ? 2019-01-20 19:37:43,267 INFO? ? : bea3107f0ef871594bedcd992265ade4: ['C:\\test\\Code\\work\\abtForCustomValue-2 - 副本 - 副本.xlsx', 'C:\\test\\Code\\work\\abtForCustomValue-2 - 副本.xlsx']
## 五、代碼獲取
如果您想獲得完整代碼或者封裝好的exe程序奇颠,可以給我留言败去。如果你有建議或者想法放航,也歡迎留言與我溝通烈拒。