Bài viết được sự cho phép của tác giả AnonyViet
Bạn quá lo âu vì có nhiều file bị trùng lặp trên ổ đĩa khiến chiếm dung lượng bộ nhớ? Nhưng khi tìm kiếm và xoá chúng theo cách thủ công lại quá tẻ nhạt. Tiếp tục Seri python, hôm nay, mình sẽ tiếp tục hướng dẫn các bạn cách xoá các File trùng lặp và giải phóng dung lượng ổ đĩa bằng python.
Giải pháp
Thay vì tìm kiếm khắp ổ đĩa để xoá các File trùng lặp, bạn có thể tự động hóa quy trình này bằng cách sử dụng script, bằng cách viết một chương trình để tìm kiếm đệ quy trong ổ đĩa và loại bỏ tất cả các File trùng lặp được tìm thấy.
Nguyên lý hoạt động
Nếu chúng ta đọc toàn bộ File và sau đó so sánh nó với các File còn lại bằng đệ quy thì sẽ mất rất nhiều thời gian, vậy chúng ta phải làm thế nào mới được?
Câu trả lời là hashing (băm), với hashing chúng ta có thể tạo ra một chuỗi các chữ cái và số nhất định đóng vai trò là danh tính của một File nhất định và nếu chúng ta tìm thấy bất kỳ File nào khác có cùng danh tính, chúng ta sẽ xóa nó.
Có rất nhiều thuật toán hashing khác nhau như:
- md5
- sha1
- sha224, sha256, sha384 và sha512
Code xoá các File trùng lặp bằng Python
Hashing trong Python khá đơn giản, chúng ta sẽ sử dụng thư viện hashlib được mặc định với thư viện chuẩn của Python.
Dưới đây là một ví dụ về cách chúng ta hashing nội dung bằng cách sử dụng hashlib, chúng ta sẽ băm một chuỗi trong Python bằng cách sử dụng thuật toán băm md5.
Ví dụ
>>> import hashlib >>> example_text = "Duplython is amazing".encode('utf-8') >>> hashlib.md5(example_text).hexdigest() '73a14f46eadcc04f4e04bec8eb66f2ab'
Giải thích chút, bạn chỉ cần import hashlib
và sau đó sử dụng phương thức md5 để tạo hash và cuối cùng sử dụng hexdigest để tạo chuỗi hash.
Ví dụ trên đã cho chúng ta thấy cách băm một chuỗi nhưng khi xem xét mối việc này với dự án sắp thực hiện, thì chúng ta phải quan tâm đến các File hơn là chuỗi đúng không? Một câu hỏi khác đã được đặt ra.
Chúng ta Hash file như thế nào?
Các File hash (băm) tương tự như chuỗi băm nhưng lại có một sự khác biệt nhỏ, trong quá trình băm File, trước tiên chúng ta cần mở File ở dạng nhị phân và sau đó băm giá trị nhị phân của File.
Băm file
Giả sử bạn có tài liệu văn bản đơn giản trên thư mục dự án của mình với tên learn.txt. Đây là cách mà chúng ta sẽ thực hiện.
>>> import hashlib >>> file = open('learn.txt', 'rb').read() >>> hashlib.md5(file).hexdigest() '0534cf6d5816c4f1ace48fff75f616c9'
Hàm này sẽ trả ra các giá trị băm giống nhau nếu các file đó có nội dung giống nhau khi đó dễ dàng tìm và Xoá các File trùng lặp bằng Python. Lưu ý: khác tên nhưng giống nội dung thì vẫn trả ra giá trị băm giống nhau nhé.
Thách thức nảy sinh khi chúng ta cố gắng đọc một File khá lớn sẽ mất một lúc để tải nó. Do đó, thay vì đợi toàn bộ File vào bộ nhớ, chúng ta có thể tiếp tục tính toán hàm băm khi đọc File.
Việc tính toán hàm băm trong khi đọc File yêu cầu chúng ta đọc File theo các khối có kích thước nhất định và liên tục cập nhật các hàm băm khi chúng ta tiếp tục đọc File cho đến khi băm hoàn chỉnh toàn bộ File. Nói đơn giản là chia file làm nhiều phần, sau đó đọc từng phần, từng phần đó sẽ được hash, sau khi hash sẽ được update vào biến khác.
Làm theo cách này có thể giúp chúng ta tiết kiệm rất nhiều thời gian chờ đợi mà chúng ta có thể sử dụng để đợi toàn bộ File sẵn sàng.
Ví dụ
>>> import hashlib >>> block_size = 1024 >>> hash = hashlib.md5() >>> with open('learn.txt', 'rb') as file: ... block = file.read(block_size) ... while len(block)>0: ... hash.update(block) ... block = file.read(block_size) ... print(hash) ... 0534cf6d5816c4f1ace48fff75f616c9
Nhưng băm chỉ là một bước chúng ta cần để thực sự loại bỏ các bản sao, do đó chúng ta sẽ sử dụng module OS để xóa các bản sao.
Chúng ta sẽ sử dụng hàm remove() trong module OS để xoá các File trùng lặp.
Sử dụng module OS để xoá file learn.txt
Ví dụ:
>>> import os >>> os.listdir() ['Desktop-File-cleaner', '.git', 'learn.txt', 'app.py', 'README.md'] >>> os.remove('learn.txt') >>> os.listdir() ['Desktop-File-cleaner', '.git', 'app.py', 'README.md']
Sau khi đã xoá được file với hàm remove(), chúng ta sẽ bắt đầu xây dựng ứng dụng.
Cách tạo ứng dụng xoá các File trùng lặp
Những thư viện cần thiết:
import time import os from hashlib import sha256
Mình là một người rất thích lập trình hướng đối tượng nên trong bài viết này, mình sẽ xây dựng tool dưới dạng một class duy nhất, code bên dưới chỉ là khung sườn của chương trình.
import time import os from hashlib import sha256 class Duplython: def __init__(self): self.home_dir = os.getcwd(); self.File_hashes = [] self.Cleaned_dirs = []; self.Total_bytes_saved = 0 self.block_size = 65536; self.count_cleaned = 0 def welcome(self)->None: print('******************************************************************') print('**************** DUPLYTHON ****************************') print('********************************************************************nn') print('---------------- WELCOME ----------------------------') time.sleep(3) print('nCleaning .................') def main(self)->None: self.welcome() if __name__ == '__main__': App = Duplython() App.main()
Đó chỉ là giao diện của chương trình, khi bạn chạy nó sẽ chỉ in lời chào mừng ra màn hình.
$ python3 app.py ****************************************************************** **************** DUPLYTHON **************************** ******************************************************************** ---------------- WELCOME ---------------------------- Cleaning .................
Bây giờ chúng ta sẽ tạo một hàm đơn giản dùng để băm một File với đường dẫn nhất định bằng cách sử dụng kiến thức băm mà chúng ta đã học ở trên.
import time import os from hashlib import sha256 class Duplython: def __init__(self): self.home_dir = os.getcwd(); self.File_hashes = [] self.Cleaned_dirs = []; self.Total_bytes_saved = 0 self.block_size = 65536; self.count_cleaned = 0 def welcome(self)->None: print('******************************************************************') print('**************** DUPLYTHON ****************************') print('********************************************************************nn') print('---------------- WELCOME ----------------------------') time.sleep(3) print('nCleaning .................') def generate_hash(self, Filename:str)->str: Filehash = sha256() try: with open(Filename, 'rb') as File: fileblock = File.read(self.block_size) while len(fileblock)>0: Filehash.update(fileblock) fileblock = File.read(self.block_size) Filehash = Filehash.hexdigest() return Filehash except: return False def main(self)->None: self.welcome() if __name__ == '__main__': App = Duplython() App.main()
Triển khai logic cho chương trình
Sau khi tạo hàm băm File, chúng ta phải triển khai ở nơi sẽ so sánh các chuỗi băm đó và loại bỏ bất kỳ bản sao nào được tìm thấy.
Tôi sẽ tạo một hàm đơn giản được gọi là clean () như hình bên dưới.
import time import os from hashlib import sha256 class Duplython: def __init__(self): self.home_dir = os.getcwd(); self.File_hashes = [] self.Cleaned_dirs = []; self.Total_bytes_saved = 0 self.block_size = 65536; self.count_cleaned = 0 def welcome(self)->None: print('******************************************************************') print('**************** DUPLYTHON ****************************') print('********************************************************************nn') print('---------------- WELCOME ----------------------------') time.sleep(3) print('nCleaning .................') def generate_hash(self, Filename:str)->str: Filehash = sha256() try: with open(Filename, 'rb') as File: fileblock = File.read(self.block_size) while len(fileblock)>0: Filehash.update(fileblock) fileblock = File.read(self.block_size) Filehash = Filehash.hexdigest() return Filehash except: return False def clean(self)->None: all_dirs = [path[0] for path in os.walk('.')] for path in all_dirs: os.chdir(path) All_Files =[file for file in os.listdir() if os.path.isfile(file)] for file in All_Files: filehash = self.generate_hash(file) if not filehash in self.File_hashes: if filehash: self.File_hashes.append(filehash) #print(file) else: byte_saved = os.path.getsize(file); self.count_cleaned+=1 self.Total_bytes_saved+=byte_saved os.remove(file); filename = file.split('/')[-1] print(filename, '.. cleaned ') os.chdir(self.home_dir) def main(self)->None: self.welcome();self.clean() if __name__ == '__main__': App = Duplython() App.main()
Bây giờ chương trình của chúng ta đã gần hoàn tất, việc cuối cùng là phải hiển thị kết quả của quá trình dọn dẹp cho người dùng xem.
Mình đã tạo ra hàm Cleaning_summary()
chỉ để làm việc đó. In kết quả của quá trình dọn dẹp ra màn hình để hoàn thành chương trình.
import time import os import shutil from hashlib import sha256 class Duplython: def __init__(self): self.home_dir = os.getcwd(); self.File_hashes = [] self.Cleaned_dirs = []; self.Total_bytes_saved = 0 self.block_size = 65536; self.count_cleaned = 0 def welcome(self)->None: print('******************************************************************') print('**************** DUPLYTHON ****************************') print('********************************************************************nn') print('---------------- WELCOME ----------------------------') time.sleep(3) print('nCleaning .................') def generate_hash(self, Filename:str)->str: Filehash = sha256() try: with open(Filename, 'rb') as File: fileblock = File.read(self.block_size) while len(fileblock)>0: Filehash.update(fileblock) fileblock = File.read(self.block_size) Filehash = Filehash.hexdigest() return Filehash except: return False def clean(self)->None: all_dirs = [path[0] for path in os.walk('.')] for path in all_dirs: os.chdir(path) All_Files =[file for file in os.listdir() if os.path.isfile(file)] for file in All_Files: filehash = self.generate_hash(file) if not filehash in self.File_hashes: if filehash: self.File_hashes.append(filehash) #print(file) else: byte_saved = os.path.getsize(file); self.count_cleaned+=1 self.Total_bytes_saved+=byte_saved os.remove(file); filename = file.split('/')[-1] print(filename, '.. cleaned ') os.chdir(self.home_dir) def cleaning_summary(self)->None: mb_saved = self.Total_bytes_saved/1048576 mb_saved = round(mb_saved, 2) print('nn--------------FINISHED CLEANING ------------') print('File cleaned : ', self.count_cleaned) print('Total Space saved : ', mb_saved, 'MB') print('-----------------------------------------------') def main(self)->None: self.welcome();self.clean();self.cleaning_summary() if __name__ == '__main__': App = Duplython() App.main()
Ứng dụng Xoá các File trùng lặp bằng Python của chúng ta đã hoàn tất, bây giờ để chạy ứng dụng, hãy chạy nó trong thư mục cụ thể mà bạn muốn dọn dẹp và nó sẽ đệ quy qua một thư mục nhất định để tìm tất cả các File và xóa File trùng lặp.
Kết quả
$ python3 app.py ****************************************************************** **************** DUPLYTHON **************************** ******************************************************************** ---------------- WELCOME ---------------------------- Cleaning ................. 0(copy).jpeg .. cleaned 0 (1)(copy).jpeg .. cleaned 0 (2)(copy).jpeg .. cleaned --------------FINISHED CLEANING ------------ File cleaned : 3 Total Space saved : 0.38 MB