python 实现文件整理分类

Posted on 2026-03-25 17:16  打杂滴  阅读(1)  评论(0)    收藏  举报

import os
import shutil
import mimetypes
from pathlib import Path
from datetime import datetime
import hashlib

class FileOrganizer:
    def __init__(self, source_dir, target_dir):
        self.source_dir = Path(source_dir)
        self.target_dir = Path(target_dir)
        self.file_types = {
            'images': ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg', '.webp'],
            'documents': ['.pdf', '.doc', '.docx', '.txt', '.rtf', '.odt', '.md', '.tex'],
            'videos': ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm'],
            'audio': ['.mp3', '.wav', '.flac', '.aac', '.ogg', '.wma'],
            'archives': ['.zip', '.rar', '.7z', '.tar', '.gz', '.bz2'],
            'executables': ['.exe', '.msi', '.bat', '.sh', '.apk'],
            'code': ['.py', '.js', '.html', '.css', '.java', '.cpp', '.c', '.php', '.rb', '.go'],
            'spreadsheets': ['.xlsx', '.xls', '.csv', '.ods'],
            'presentations': ['.pptx', '.ppt', '.odp']
        }
   
    def get_file_type(self, file_path):
        """根据文件扩展名确定文件类型"""
        ext = file_path.suffix.lower()
        for category, extensions in self.file_types.items():
            if ext in extensions:
                return category
        return 'others'
   
    def organize_by_extension(self):
        """按文件扩展名分类整理"""
        organized_count = 0
        for file_path in self.source_dir.rglob('*'):
            if file_path.is_file():
                file_type = self.get_file_type(file_path)
                target_folder = self.target_dir / file_type
                target_folder.mkdir(exist_ok=True)
               
                target_path = target_folder / file_path.name
                # 处理重名文件
                if target_path.exists():
                    name_without_ext = file_path.stem
                    ext = file_path.suffix
                    counter = 1
                    while target_path.exists():
                        new_name = f"{name_without_ext}_{counter}{ext}"
                        target_path = target_folder / new_name
                        counter += 1
               
                shutil.move(str(file_path), str(target_path))
                organized_count += 1
                print(f"已移动: {file_path.name} -> {file_type}/")
       
        print(f"按扩展名分类完成,共整理 {organized_count} 个文件")
   
    def organize_by_date(self):
        """按修改日期分类整理"""
        organized_count = 0
        for file_path in self.source_dir.rglob('*'):
            if file_path.is_file():
                # 获取文件修改时间
                mtime = file_path.stat().st_mtime
                date_folder = datetime.fromtimestamp(mtime).strftime('%Y-%m')
               
                target_folder = self.target_dir / date_folder
                target_folder.mkdir(exist_ok=True)
               
                target_path = target_folder / file_path.name
                # 处理重名文件
                if target_path.exists():
                    name_without_ext = file_path.stem
                    ext = file_path.suffix
                    counter = 1
                    while target_path.exists():
                        new_name = f"{name_without_ext}_{counter}{ext}"
                        target_path = target_folder / new_name
                        counter += 1
               
                shutil.move(str(file_path), str(target_path))
                organized_count += 1
                print(f"已移动: {file_path.name} -> {date_folder}/")
       
        print(f"按日期分类完成,共整理 {organized_count} 个文件")
   
    def organize_by_size(self):
        """按文件大小分类整理"""
        organized_count = 0
        size_categories = {
            'small': (0, 1024*1024),      # < 1MB
            'medium': (1024*1024, 10*1024*1024),  # 1MB - 10MB
            'large': (10*1024*1024, 100*1024*1024), # 10MB - 100MB
            'huge': (100*1024*1024, float('inf'))   # > 100MB
        }
       
        for file_path in self.source_dir.rglob('*'):
            if file_path.is_file():
                file_size = file_path.stat().st_size
               
                # 确定文件大小类别
                size_category = 'others'
                for category, (min_size, max_size) in size_categories.items():
                    if min_size <= file_size < max_size:
                        size_category = category
                        break
               
                target_folder = self.target_dir / size_category
                target_folder.mkdir(exist_ok=True)
               
                target_path = target_folder / file_path.name
                # 处理重名文件
                if target_path.exists():
                    name_without_ext = file_path.stem
                    ext = file_path.suffix
                    counter = 1
                    while target_path.exists():
                        new_name = f"{name_without_ext}_{counter}{ext}"
                        target_path = target_folder / new_name
                        counter += 1
               
                shutil.move(str(file_path), str(target_path))
                organized_count += 1
                print(f"已移动: {file_path.name} -> {size_category}/")
       
        print(f"按大小分类完成,共整理 {organized_count} 个文件")
   
    def remove_duplicates(self):
        """查找并删除重复文件"""
        file_hashes = {}
        duplicates = []
       
        for file_path in self.source_dir.rglob('*'):
            if file_path.is_file():
                # 计算文件哈希值
                hash_value = self.calculate_file_hash(file_path)
               
                if hash_value in file_hashes:
                    duplicates.append(file_path)
                    print(f"发现重复文件: {file_path} (与 {file_hashes[hash_value]} 相同)")
                else:
                    file_hashes[hash_value] = file_path
       
        # 删除重复文件
        for duplicate in duplicates:
            duplicate.unlink()
            print(f"已删除重复文件: {duplicate}")
       
        print(f"共删除 {len(duplicates)} 个重复文件")
   
    def calculate_file_hash(self, file_path):
        """计算文件的MD5哈希值"""
        hash_md5 = hashlib.md5()
        with open(file_path, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
        return hash_md5.hexdigest()
   
    def create_folder_structure(self):
        """创建目标文件夹结构"""
        for folder_name in self.file_types.keys():
            folder_path = self.target_dir / folder_name
            folder_path.mkdir(exist_ok=True)
        (self.target_dir / 'others').mkdir(exist_ok=True)

def main():
    print("=== 文件整理分类工具 ===")
    source_dir = input("请输入源文件夹路径: ").strip()
    target_dir = input("请输入目标文件夹路径: ").strip()
   
    if not os.path.exists(source_dir):
        print("源文件夹不存在!")
        return
   
    # 创建目标文件夹
    Path(target_dir).mkdir(exist_ok=True)
   
    organizer = FileOrganizer(source_dir, target_dir)
   
    print("\n请选择整理方式:")
    print("1. 按文件类型整理")
    print("2. 按修改日期整理")
    print("3. 按文件大小整理")
    print("4. 删除重复文件")
   
    choice = input("请输入选择(1-4): ").strip()
   
    if choice == '1':
        organizer.create_folder_structure()
        organizer.organize_by_extension()
    elif choice == '2':
        organizer.organize_by_date()
    elif choice == '3':
        organizer.organize_by_size()
    elif choice == '4':
        organizer.remove_duplicates()
    else:
        print("无效选择!")

if __name__ == "__main__":
    main()
 
-------------------------
按文件类型将指定目录   整理到新的目录  
......

已移动: e5a357374617d7e26e154b02192b9c3b6d2c68 -> others/
已移动: pack-e43316fd640c7b070de19960b7d3d68f265b81e8.idx -> others/
已移动: pack-e43316fd640c7b070de19960b7d3d68f265b81e8.pack -> others/
已移动: Branch_3303ef41 -> others/
已移动: lh -> others/
已移动: lh_1202 -> others/
已移动: main -> others/
已移动: master -> others/
已移动: cxn -> others/
已移动: HEAD -> others/
已移动: heyong -> others/
已移动: lh -> others/
已移动: master -> others/
已移动: release -> others/
已移动: config -> others/
已移动: description -> others/
已移动: HEAD -> others/
已移动: index -> others/
已移动: packed-refs -> others/
已移动: applypatch-msg.sample -> others/
已移动: commit-msg.sample -> others/
已移动: fsmonitor-watchman.sample -> others/
已移动: post-update.sample -> others/
已移动: pre-applypatch.sample -> others/
已移动: pre-commit.sample -> others/
已移动: pre-merge-commit.sample -> others/
已移动: pre-push.sample -> others/
已移动: pre-rebase.sample -> others/
已移动: pre-receive.sample -> others/
已移动: prepare-commit-msg.sample -> others/
已移动: push-to-checkout.sample -> others/
已移动: update.sample -> others/
已移动: exclude -> others/
已移动: HEAD -> others/
已移动: main -> others/
已移动: HEAD -> others/
已移动: pack-74037bbe88c4f7f0b97589614087317d7de79f15.idx -> others/
已移动: pack-74037bbe88c4f7f0b97589614087317d7de79f15.pack -> others/
已移动: main -> others/
已移动: HEAD -> others/
已移动: config -> others/
已移动: description -> others/
已移动: HEAD -> others/
已移动: index -> others/
已移动: packed-refs -> others/
已移动: applypatch-msg.sample -> others/
已移动: commit-msg.sample -> others/
已移动: fsmonitor-watchman.sample -> others/
已移动: post-update.sample -> others/
已移动: pre-applypatch.sample -> others/
已移动: pre-commit.sample -> others/
已移动: pre-merge-commit.sample -> others/
已移动: pre-push.sample -> others/
已移动: pre-rebase.sample -> others/
已移动: pre-receive.sample -> others/
已移动: prepare-commit-msg.sample -> others/
已移动: push-to-checkout.sample -> others/
已移动: update.sample -> others/
已移动: exclude -> others/
已移动: HEAD -> others/
已移动: main -> others/
已移动: HEAD -> others/
已移动: pack-2d927b0199267a3e96e7062163724770ea2233e0.idx -> others/
已移动: pack-2d927b0199267a3e96e7062163724770ea2233e0.pack -> others/
已移动: main -> others/
已移动: HEAD -> others/
已移动: config -> others/
已移动: description -> others/
已移动: HEAD -> others/
已移动: index -> others/
已移动: packed-refs -> others/
已移动: applypatch-msg.sample -> others/
已移动: commit-msg.sample -> others/
已移动: fsmonitor-watchman.sample -> others/
已移动: post-update.sample -> others/
已移动: pre-applypatch.sample -> others/
已移动: pre-commit.sample -> others/
已移动: pre-merge-commit.sample -> others/
已移动: pre-push.sample -> others/
已移动: pre-rebase.sample -> others/
已移动: pre-receive.sample -> others/
已移动: prepare-commit-msg.sample -> others/
已移动: push-to-checkout.sample -> others/
已移动: update.sample -> others/
已移动: exclude -> others/
已移动: HEAD -> others/
已移动: main -> others/
已移动: HEAD -> others/
已移动: pack-6a0f40474fc6d94e2c7cb426a8868c6b28666ca5.idx -> others/
已移动: pack-6a0f40474fc6d94e2c7cb426a8868c6b28666ca5.pack -> others/
已移动: main -> others/
已移动: HEAD -> others/
按扩展名分类完成,共整理 30917 个文件

结果如下:

image

 

image

 

博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3