Python实现本地文件类型统计与可视化工具


背景介绍

在日常工作中,我们经常会遇到需要整理本地文件或分析项目结构的场景:
– 想知道项目目录中代码文件(.py/.java)与资源文件(.png/.json)的占比
– 需要清理磁盘空间,找出占用空间最大的文件类型
– 需要统计文档目录中文本文件与表格文件的分布

手动统计这些信息既耗时又容易出错,因此开发一个自动化的文件类型统计工具非常有必要。本文将介绍如何用Python实现一个本地文件类型统计与可视化工具,支持递归扫描目录、生成文本报告和可视化图表。

思路分析

工具实现分为五个核心模块:

  1. 命令行参数解析: 使用argparse获取用户指定的目标目录
  2. 目录递归遍历: 使用os.walk遍历目录下所有文件(包括子目录)
  3. 文件类型统计: 提取文件扩展名,统计每种类型的数量和总大小
  4. 文本报告生成: 格式化输出统计结果到控制台
  5. 可视化图表生成: 使用matplotlib绘制饼图(数量占比)和柱状图(大小占比)

代码实现

环境准备

需要安装matplotlib库用于可视化:

pip install matplotlib

完整代码

import os
import argparse
import math
import matplotlib.pyplot as plt


def convert_size(size_bytes):
    """
    将字节数转换为易读的单位(KB/MB/GB等)

    Args:
        size_bytes (int): 文件大小(字节)

    Returns: 
        str: 格式化后的大小字符串(保留一位小数)

    Example:
        convert_size(1234567) → "1.2 MB"

    """

    if size_bytes == 0:
        return "0 B"

    # 定义单位转换级别与对应标签
    size_units = ['B', 'KB', 'MB', 'GB', 'TB']
    # 计算对应的单位级别(取对数)
    unit_index = math.floor(math.log(size_bytes, 1024))
    # 避免超过最大单位(TB)
    unit_index = min(unit_index, len(size_units)-1)
    # 转换为对应单位的值并保留一位小数
    converted_size = round(size_bytes / (1024 ** unit_index), 1)

    return f"{converted_size} {size_units[unit_index]}"


def collect_stats(target_dir):
    """
    递归扫描目录,统计文件类型信息

    Args:
        target_dir (str): 目标目录路径

    Returns:
        dict: 统计结果字典,包含:
            - total_files: 总文件数
            - total_size: 总大小(字节)
            - type_stats: 每种类型的详细统计(数量、大小)

    """

    # 初始化统计结果
    stats = {
        "total_files": 0,
        "total_size": 0,
        "type_stats": {}
    }

    # 递归遍历目录
    for root, _, files in os.walk(target_dir):
        for file_name in files:
            # 获取文件完整路径
            file_path = os.path.join(root, file_name)

            try:
                # 获取文件大小
                file_size = os.path.getsize(file_path)
            except (OSError, PermissionError):
                # 跳过无法访问的文件
                continue

            # 更新总统计
            stats["total_files"] +=1
            stats["total_size"] += file_size

            # 提取文件扩展名(统一转为小写)
            _, ext = os.path.splitext(file_name)
            ext = ext.lower()

            # 处理无扩展名的文件
            if not ext:
                ext = "无扩展名"
            else:
                # 去掉扩展名前的点(如.py → py)
                ext = ext[1:]

            # 更新类型统计
            if ext not in stats["type_stats"]:
                stats["type_stats"][ext] = {"count":0, "size":0}

            stats["type_stats"][ext]["count"] +=1
            stats["type_stats"][ext]["size"] += file_size

    return stats


def generate_text_report(stats, target_dir):
    """
    生成控制台文本报告

    Args:
        stats (dict): collect_stats返回的统计结果
        target_dir (str): 扫描目录路径

    """

    print("\n=== 文件类型统计报告 ===")
    print(f"扫描目录: {target_dir}")
    print(f"总文件数: {stats['total_files']}")
    print(f"总大小: {convert_size(stats['total_size'])}\n")

    # 按文件数量降序排序
    sorted_types = sorted(
        stats["type_stats"].items(), 
        key=lambda x: -x[1]["count"]
    )

    print("按数量排序:")
    for idx, (ext, data) in enumerate(sorted_types[:10], 1):  # 显示前10种类型
        count = data["count"]
        size = data["size"]

        # 计算占比
        count_ratio = round(count / stats["total_files"] *100,1)
        size_ratio = round(size / stats["total_size"] *100,1)

        # 格式化输出
        print(f"{idx}. .{ext} → {count}个({count_ratio}%), 总大小: {convert_size(size)}({size_ratio}%)")

    # 处理剩余类型
    if len(sorted_types) >10:
        remaining_count = sum(x[1]["count"] for x in sorted_types[10:])
        remaining_size = sum(x[1]["size"] for x in sorted_types[10:])
        count_ratio = round(remaining_count / stats["total_files"] *100,1)
        size_ratio = round(remaining_size / stats["total_size"] *100,1)
        print(f"{11}. 其他 → {remaining_count}个({count_ratio}%), 总大小: {convert_size(remaining_size)}({size_ratio}%)")


def generate_visualization(stats, output_path="file_stats_charts.png"):
    """
    生成可视化图表并保存

    Args:
        stats (dict): collect_stats返回的统计结果
        output_path (str): 图表保存路径

    """

    # 准备数据(取前5种类型+其他)
    sorted_types = sorted(
        stats["type_stats"].items(), 
        key=lambda x: -x[1]["count"]
    )

    top_types = sorted_types[:5]
    others_count = sum(x[1]["count"] for x in sorted_types[5:])
    others_size = sum(x[1]["size"] for x in sorted_types[5:])

    # 添加"其他"类型(如果有)
    if others_count >0:
        top_types.append(("其他", {"count": others_count, "size": others_size}))

    # 提取标签和数据
    labels = [f".{ext}" if ext != "无扩展名" else "无扩展名" for ext, _ in top_types]
    counts = [data["count"] for _, data in top_types]
    sizes = [data["size"] for _, data in top_types]

    # 计算大小占比
    size_ratios = [round(s / stats["total_size"] *100,1) for s in sizes]

    # 创建图表(1行2列)
    fig, (ax1, ax2) = plt.subplots(1,2, figsize=(15,6))

    # 左图: 数量占比饼图
    ax1.pie(counts, labels=labels, autopct='%1.1f%%', startangle=90)
    ax1.set_title("文件类型数量占比")

    # 右图: 大小占比柱状图
    bars = ax2.bar(labels, size_ratios, color='skyblue')
    ax2.set_title("文件类型大小占比(%)")
    ax2.set_xlabel("文件类型")
    ax2.set_ylabel("占比(%)")

    # 添加数值标签
    for bar in bars:
        height = bar.get_height()
        ax2.text(bar.get_x() + bar.get_width()/2., height+0.5, f"{height}%", ha='center')

    # 旋转x轴标签避免重叠
    plt.setp(ax2.xaxis.get_majorticklabels(), rotation=45, ha='right')

    # 调整布局并保存
    plt.tight_layout()
    plt.savefig(output_path, dpi=100)
    plt.close()

    print(f"\n统计完成!可视化图表已保存至: {output_path}")


def main():
    """
    主函数: 解析参数→收集统计→生成报告→可视化
    """

    # 解析命令行参数
    parser = argparse.ArgumentParser(description="本地文件类型统计与可视化工具")
    parser.add_argument(
        "--dir", 
        default=os.getcwd(), 
        help="目标目录路径(默认: 当前目录)"
    )
    args = parser.parse_args()

    # 验证目录有效性
    if not os.path.isdir(args.dir):
        print(f"错误: {args.dir}不是有效目录")
        return

    # 收集统计数据
    print(f"正在扫描目录: {args.dir}...")
    stats = collect_stats(args.dir)

    if stats["total_files"] ==0:
        print("未找到任何文件")
        return

    # 生成文本报告
    generate_text_report(stats, args.dir)

    # 生成可视化图表
    generate_visualization(stats)


if __name__ == "__main__":
    main()

使用说明

  1. 安装依赖:
    pip install matplotlib
    
  2. 运行工具:
    • 扫描当前目录:
      bash
      python file_stats.py
    • 扫描指定目录:
      bash
      python file_stats.py --dir /path/to/your/directory
  3. 输出结果:
    • 控制台显示文本报告
    • 生成file_stats_charts.png图表文件

总结

本文实现的工具具有以下特点:
自动化: 递归扫描目录,无需手动干预
全面: 统计文件数量、大小及占比
直观: 同时提供文本报告和可视化图表
健壮: 处理无法访问的文件和权限问题

扩展方向:
– 支持过滤特定目录(如node_modules/.git)
– 导出CSV格式报告
– 添加文件大小排序选项
– 支持更多图表类型(如折线图、堆叠柱状图)

这个工具不仅能帮助我们快速了解文件分布,还可以作为Python文件操作、数据处理和可视化的学习案例。希望本文对你有所帮助!


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注