0
点赞
收藏
分享

微信扫一扫

python文件及目录的比较

import os import hashlib import time from datetime import datetime import argparse

class FileComparator: """文件比较工具,用于比较文件内容和属性"""

def __init__(self, buffer_size=65536):
    """初始化比较器
    
    Args:
        buffer_size: 读取文件时的缓冲区大小
    """
    self.buffer_size = buffer_size

def get_file_hash(self, file_path, algorithm='sha256'):
    """计算文件的哈希值
    
    Args:
        file_path: 文件路径
        algorithm: 哈希算法,默认为sha256
        
    Returns:
        文件的哈希值
    """
    if not os.path.isfile(file_path):
        raise ValueError(f"不是有效的文件: {file_path}")
    
    hash_func = getattr(hashlib, algorithm, None)
    if not hash_func:
        raise ValueError(f"不支持的哈希算法: {algorithm}")
    
    hash_obj = hash_func()
    
    try:
        with open(file_path, 'rb') as f:
            while True:
                data = f.read(self.buffer_size)
                if not data:
                    break
                hash_obj.update(data)
        return hash_obj.hexdigest()
    except Exception as e:
        raise Exception(f"计算哈希值时出错: {str(e)}")

def compare_file_content(self, file1, file2, algorithm='sha256'):
    """比较两个文件的内容是否相同
    
    Args:
        file1: 第一个文件路径
        file2: 第二个文件路径
        algorithm: 哈希算法
        
    Returns:
        布尔值,表示两个文件内容是否相同
    """
    hash1 = self.get_file_hash(file1, algorithm)
    hash2 = self.get_file_hash(file2, algorithm)
    return hash1 == hash2

def get_file_info(self, file_path):
    """获取文件的基本信息
    
    Args:
        file_path: 文件路径
        
    Returns:
        包含文件信息的字典
    """
    if not os.path.exists(file_path):
        return None
        
    info = {
        'path': file_path,
        'size': os.path.getsize(file_path),
        'modified_time': os.path.getmtime(file_path),
        'modified_time_str': datetime.fromtimestamp(os.path.getmtime(file_path)).strftime('%Y-%m-%d %H:%M:%S'),
        'created_time': os.path.getctime(file_path) if hasattr(os, 'getctime') else None,
        'created_time_str': datetime.fromtimestamp(os.path.getctime(file_path)).strftime('%Y-%m-%d %H:%M:%S') if hasattr(os, 'getctime') else None,
        'is_file': os.path.isfile(file_path),
        'is_dir': os.path.isdir(file_path)
    }
    return info

def compare_file_properties(self, file1, file2, properties=None):
    """比较两个文件的属性
    
    Args:
        file1: 第一个文件路径
        file2: 第二个文件路径
        properties: 要比较的属性列表,默认为['size', 'modified_time']
        
    Returns:
        包含比较结果的字典
    """
    if properties is None:
        properties = ['size', 'modified_time']
        
    info1 = self.get_file_info(file1)
    info2 = self.get_file_info(file2)
    
    if not info1 or not info2:
        raise ValueError("无法获取文件信息")
        
    result = {
        'same': True,
        'differences': {}
    }
    
    for prop in properties:
        if prop not in info1 or prop not in info2:
            continue
            
        if info1[prop] != info2[prop]:
            result['same'] = False
            result['differences'][prop] = {
                file1: info1[prop],
                file2: info2[prop]
            }
            
    return result

class DirectoryComparator: """目录比较工具,用于比较两个目录的结构和内容"""

def __init__(self, file_comparator=None):
    """初始化目录比较器
    
    Args:
        file_comparator: 文件比较器实例,默认为None,将创建新实例
    """
    self.file_comparator = file_comparator or FileComparator()

def compare_directories(self, dir1, dir2, compare_content=True, ignore_patterns=None):
    """递归比较两个目录
    
    Args:
        dir1: 第一个目录路径
        dir2: 第二个目录路径
        compare_content: 是否比较文件内容
        ignore_patterns: 忽略的文件/目录模式列表
        
    Returns:
        包含比较结果的字典
    """
    if not os.path.isdir(dir1) or not os.path.isdir(dir2):
        raise ValueError("参数必须是有效的目录")
        
    if ignore_patterns is None:
        ignore_patterns = []
        
    result = {
        'only_in_dir1': [],
        'only_in_dir2': [],
        'different_files': [],
        'same_files': [],
        'different_dirs': []
    }
    
    # 获取两个目录中的所有条目
    entries1 = set(os.listdir(dir1))
    entries2 = set(os.listdir(dir2))
    
    # 应用忽略模式
    def should_ignore(entry):
        for pattern in ignore_patterns:
            if pattern in entry:
                return True
        return False
        
    entries1 = {e for e in entries1 if not should_ignore(e)}
    entries2 = {e for e in entries2 if not should_ignore(e)}
    
    # 找出只在第一个目录中的条目
    only_in_dir1 = entries1 - entries2
    for entry in only_in_dir1:
        full_path = os.path.join(dir1, entry)
        result['only_in_dir1'].append(full_path)
        
    # 找出只在第二个目录中的条目
    only_in_dir2 = entries2 - entries1
    for entry in only_in_dir2:
        full_path = os.path.join(dir2, entry)
        result['only_in_dir2'].append(full_path)
        
    # 比较两个目录中都存在的条目
    common_entries = entries1 & entries2
    for entry in common_entries:
        path1 = os.path.join(dir1, entry)
        path2 = os.path.join(dir2, entry)
        
        if os.path.isdir(path1) and os.path.isdir(path2):
            # 递归比较子目录
            sub_result = self.compare_directories(
                path1, path2, compare_content, ignore_patterns
            )
            
            # 如果子目录有差异,记录下来
            if (sub_result['only_in_dir1'] or sub_result['only_in_dir2'] or 
                sub_result['different_files'] or sub_result['different_dirs']):
                result['different_dirs'].append(path1)
                
            # 合并子目录的比较结果
            for key in ['only_in_dir1', 'only_in_dir2', 'different_files', 'same_files', 'different_dirs']:
                result[key].extend(sub_result[key])
                
        elif os.path.isfile(path1) and os.path.isfile(path2):
            # 比较文件
            if compare_content:
                are_same = self.file_comparator.compare_file_content(path1, path2)
            else:
                prop_result = self.file_comparator.compare_file_properties(path1, path2)
                are_same = prop_result['same']
                
            if are_same:
                result['same_files'].append(path1)
            else:
                result['different_files'].append(path1)
                
        else:
            # 一个是文件,一个是目录
            result['different_files'].append(path1)
            
    return result

def generate_report(self, result, output_file=None):
    """生成比较报告
    
    Args:
        result: 比较结果
        output_file: 输出文件路径,默认为None,将打印到控制台
        
    Returns:
        报告内容
    """
    report = []
    report.append("=" * 50)
    report.append("目录比较报告")
    report.append("生成时间: " + datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
    report.append("=" * 50)
    
    report.append("\n只在第一个目录中的条目:")
    if result['only_in_dir1']:
        for item in result['only_in_dir1']:
            report.append(f"- {item}")
    else:
        report.append("- 无")
        
    report.append("\n只在第二个目录中的条目:")
    if result['only_in_dir2']:
        for item in result['only_in_dir2']:
            report.append(f"- {item}")
    else:
        report.append("- 无")
        
    report.append("\n内容不同的文件:")
    if result['different_files']:
        for item in result['different_files']:
            report.append(f"- {item}")
    else:
        report.append("- 无")
        
    report.append("\n内容相同的文件:")
    if result['same_files']:
        report.append(f"- 共 {len(result['same_files'])} 个文件")
    else:
        report.append("- 无")
        
    report.append("\n结构不同的子目录:")
    if result['different_dirs']:
        for item in result['different_dirs']:
            report.append(f"- {item}")
    else:
        report.append("- 无")
        
    report.append("\n" + "=" * 50)
    report.append("比较总结")
    report.append(f"只在第一个目录中的条目: {len(result['only_in_dir1'])}")
    report.append(f"只在第二个目录中的条目: {len(result['only_in_dir2'])}")
    report.append(f"内容不同的文件: {len(result['different_files'])}")
    report.append(f"内容相同的文件: {len(result['same_files'])}")
    report.append(f"结构不同的子目录: {len(result['different_dirs'])}")
    report.append("=" * 50)
    
    report_content = "\n".join(report)
    
    if output_file:
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(report_content)
        print(f"报告已保存到: {output_file}")
    else:
        print(report_content)
        
    return report_content

def main(): """主函数,处理命令行参数并执行比较""" parser = argparse.ArgumentParser(description='比较两个文件或目录') parser.add_argument('path1', help='第一个文件或目录路径') parser.add_argument('path2', help='第二个文件或目录路径') parser.add_argument('-c', '--compare-content', action='store_true', help='比较文件内容(默认为比较文件属性)') parser.add_argument('-o', '--output', help='输出报告文件路径') parser.add_argument('-i', '--ignore', nargs='+', help='忽略的文件/目录模式列表')

args = parser.parse_args()

try:
    if os.path.isfile(args.path1) and os.path.isfile(args.path2):
        # 文件比较
        file_comp = FileComparator()
        
        if args.compare_content:
            are_same = file_comp.compare_file_content(args.path1, args.path2)
            print(f"文件内容是否相同: {'是' if are_same else '否'}")
        else:
            result = file_comp.compare_file_properties(args.path1, args.path2)
            print(f"文件属性是否相同: {'是' if result['same'] else '否'}")
            
            if not result['same']:
                print("不同的属性:")
                for prop, values in result['differences'].items():
                    print(f"- {prop}:")
                    print(f"  {args.path1}: {values[args.path1]}")
                    print(f"  {args.path2}: {values[args.path2]}")
            
        # 输出文件信息
        info1 = file_comp.get_file_info(args.path1)
        info2 = file_comp.get_file_info(args.path2)
        
        print("\n文件1信息:")
        for key, value in info1.items():
            if key not in ['path', 'is_file', 'is_dir']:
                print(f"- {key}: {value}")
                
        print("\n文件2信息:")
        for key, value in info2.items():
            if key not in ['path', 'is_file', 'is_dir']:
                print(f"- {key}: {value}")
                
    elif os.path.isdir(args.path1) and os.path.isdir(args.path2):
        # 目录比较
        dir_comp = DirectoryComparator()
        result = dir_comp.compare_directories(
            args.path1, args.path2, 
            compare_content=args.compare_content,
            ignore_patterns=args.ignore
        )
        
        dir_comp.generate_report(result, args.output)
        
    else:
        print("错误: 请提供两个文件或两个目录进行比较")
        parser.print_help()
        
except Exception as e:
    print(f"发生错误: {str(e)}")

if name == "main": main()

举报

相关推荐

0 条评论