统计springboot项目及各模块代码量:行数、实际行数、注释行数、空白行数

统计springboot项目及各模块代码量:行数、实际行数、注释行数、空白行数

效果

代码统计报告(按模块):


文件总数: 612
代码总行数: 66213
实际代码行: 42599
注释行数: 15849
空白行数: 7765


各模块代码统计:
【admin】 文件数: 25, 总行数: 3152, 代码行: 2250, 注释行: 608, 空白行: 294
【common】 文件数: 110, 总行数: 12090, 代码行: 7308, 注释行: 3152, 空白行: 1630
【core】 文件数: 71, 总行数: 6242, 代码行: 3379, 注释行: 2065, 空白行: 798
【framework】 文件数: 56, 总行数: 5566, 代码行: 3041, 注释行: 2030, 空白行: 495
【generator】 文件数: 17, 总行数: 3006, 代码行: 2005, 注释行: 653, 空白行: 348
【quartz】 文件数: 21, 总行数: 2093, 代码行: 1265, 注释行: 608, 空白行: 220
【system】 文件数: 61, 总行数: 7412, 代码行: 4052, 注释行: 2480, 空白行: 880
【base】 文件数: 250, 总行数: 26204, 代码行: 18907, 注释行: 4253, 空白行: 3044


各类型文件统计:
.sql 文件: 22 个, 827 行
.js 文件: 1 个, 5976 行
.java 文件: 527 个, 53003 行
.yml 文件: 5 个, 825 行
.xml 文件: 57 个, 5582 行

代码预览

运行代码

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import java.util.Map;
import java.util.TreeMap;

/**
 * SpringBoot项目代码行数统计工具
 * @author Administrator
 */
public class CodeLineCounter {

    // 要统计的文件类型
    private static final String[] FILE_TYPES = {
        ".java", ".xml", ".yml", ".properties", ".html", ".js", ".css", ".sql"
    };

    // 不需要统计的目录
    private static final String[] EXCLUDE_FOLDERS = {
        "target", "bin", ".git", ".idea", ".mvn", "node_modules"
    };

    // 统计结果
    private static class StatisticsResult {

        int totalFiles = 0;         // 文件总数
        int totalLines = 0;         // 总行数
        int codeLines = 0;          // 代码行
        int commentLines = 0;       // 注释行
        int blankLines = 0;         // 空白行
        Map<String, Integer> fileTypeCount = new HashMap<>();     // 各类型文件数量
        Map<String, Integer> fileTypeLines = new HashMap<>();     // 各类型文件行数
        Map<String, ModuleStatistics> moduleStats = new TreeMap<>(); // 按模块统计
    }

    // 模块统计信息
    private static class ModuleStatistics {

        String moduleName;          // 模块名称
        int files = 0;              // 文件数
        int totalLines = 0;         // 总行数
        int codeLines = 0;          // 代码行
        int commentLines = 0;       // 注释行
        int blankLines = 0;         // 空白行

        public ModuleStatistics(String name) {
            this.moduleName = name;
        }
    }

    /**
     * 统计项目代码量
     *
     * @param projectPath 项目根目录路径
     * @return 统计结果描述
     */
    public static String countCodeLines(String projectPath) {
        StatisticsResult result = new StatisticsResult();
        File projectDir = new File(projectPath);
        if (!projectDir.exists() || !projectDir.isDirectory()) {
            return "无效的项目路径";
        }

        // 开始统计
        countDirectory(projectDir, result);

        // 生成统计报告
        StringBuilder report = new StringBuilder();
        report.append("代码统计报告:\n");
        report.append("----------------------------------------\n");
        report.append("文件总数: ").append(result.totalFiles).append("\n");
        report.append("代码总行数: ").append(result.totalLines).append("\n");
        report.append("实际代码行: ").append(result.codeLines).append("\n");
        report.append("注释行数: ").append(result.commentLines).append("\n");
        report.append("空白行数: ").append(result.blankLines).append("\n");
        report.append("----------------------------------------\n");
        report.append("各类型文件统计:\n");

        for (String fileType : result.fileTypeCount.keySet()) {
            report.append(fileType).append(" 文件: ")
                    .append(result.fileTypeCount.get(fileType)).append(" 个, ")
                    .append(result.fileTypeLines.getOrDefault(fileType, 0)).append(" 行\n");
        }

        return report.toString();
    }

    /**
     * 统计项目代码量(按模块)
     *
     * @param projectPath 项目根目录路径
     * @return 统计结果描述
     */
    public static String countCodeLinesByModule(String projectPath) {
        StatisticsResult result = new StatisticsResult();
        File projectDir = new File(projectPath);
        if (!projectDir.exists() || !projectDir.isDirectory()) {
            return "无效的项目路径";
        }

        // 开始统计
        countDirectory(projectDir, result);

        // 生成统计报告
        StringBuilder report = new StringBuilder();
        report.append("代码统计报告(按模块):\n");
        report.append("----------------------------------------\n");
        report.append("文件总数: ").append(result.totalFiles).append("\n");
        report.append("代码总行数: ").append(result.totalLines).append("\n");
        report.append("实际代码行: ").append(result.codeLines).append("\n");
        report.append("注释行数: ").append(result.commentLines).append("\n");
        report.append("空白行数: ").append(result.blankLines).append("\n");
        report.append("----------------------------------------\n");
        report.append("各模块代码统计:\n");

        for (String moduleName : result.moduleStats.keySet()) {
            ModuleStatistics ms = result.moduleStats.get(moduleName);
            report.append("【").append(moduleName).append("】")
                    .append(" 文件数: ").append(ms.files)
                    .append(", 总行数: ").append(ms.totalLines)
                    .append(", 代码行: ").append(ms.codeLines)
                    .append(", 注释行: ").append(ms.commentLines)
                    .append(", 空白行: ").append(ms.blankLines)
                    .append("\n");
        }

        report.append("----------------------------------------\n");
        report.append("各类型文件统计:\n");

        for (String fileType : result.fileTypeCount.keySet()) {
            report.append(fileType).append(" 文件: ")
                    .append(result.fileTypeCount.get(fileType)).append(" 个, ")
                    .append(result.fileTypeLines.getOrDefault(fileType, 0)).append(" 行\n");
        }

        return report.toString();
    }

    /**
     * 递归统计目录
     */
    private static void countDirectory(File directory, StatisticsResult result) {
        File[] files = directory.listFiles();
        if (files == null) {
            return;
        }

        for (File file : files) {
            // 如果是需要排除的目录,则跳过
            if (file.isDirectory()) {
                if (shouldExclude(file.getName())) {
                    continue;
                }
                countDirectory(file, result);
            } else {
                String fileType = getFileExtension(file.getName());
                if (isTargetFileType(fileType)) {
                    // 统计单个文件
                    Map<String, Integer> fileCounts = countSingleFile(file);

                    // 更新统计结果
                    result.totalFiles++;
                    result.totalLines += fileCounts.get("total");
                    result.codeLines += fileCounts.get("code");
                    result.commentLines += fileCounts.get("comment");
                    result.blankLines += fileCounts.get("blank");

                    // 更新文件类型统计
                    result.fileTypeCount.put(fileType,
                            result.fileTypeCount.getOrDefault(fileType, 0) + 1);
                    result.fileTypeLines.put(fileType,
                            result.fileTypeLines.getOrDefault(fileType, 0) + fileCounts.get("total"));

                    // 更新模块统计
                    String moduleName = getModuleName(file.getAbsolutePath());
                    if (!result.moduleStats.containsKey(moduleName)) {
                        result.moduleStats.put(moduleName, new ModuleStatistics(moduleName));
                    }

                    ModuleStatistics ms = result.moduleStats.get(moduleName);
                    ms.files++;
                    ms.totalLines += fileCounts.get("total");
                    ms.codeLines += fileCounts.get("code");
                    ms.commentLines += fileCounts.get("comment");
                    ms.blankLines += fileCounts.get("blank");
                }
            }
        }
    }

    /**
     * 获取文件所属模块名
     */
    private static String getModuleName(String filePath) {
        File file = new File(filePath);
        String path = file.getAbsolutePath();

        // 尝试判断模块位置
        String[] modulePrefixes = {
            "ezw-", "manage-"
        };

        for (String prefix : modulePrefixes) {
            int index = path.indexOf(prefix);
            if (index >= 0) {
                int endIndex = path.indexOf(File.separator, index);
                if (endIndex > index) {
                    return path.substring(index, endIndex);
                } else {
                    // 如果没有下一级目录,则取到结尾
                    return path.substring(index);
                }
            }
        }

        // 如果无法确定模块,则返回文件所在上级目录的名称
        return file.getParentFile().getName();
    }

    /**
     * 统计单个文件的行数
     */
    private static Map<String, Integer> countSingleFile(File file) {
        Map<String, Integer> counts = new HashMap<>();
        int totalLines = 0;
        int codeLines = 0;
        int commentLines = 0;
        int blankLines = 0;

        boolean inBlockComment = false;

        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = reader.readLine()) != null) {
                totalLines++;
                line = line.trim();

                // 空行
                if (line.isEmpty()) {
                    blankLines++;
                    continue;
                }

                // 处理注释
                if (inBlockComment) {
                    commentLines++;
                    if (line.contains("*/")) {
                        inBlockComment = false;
                    }
                } else if (line.startsWith("//")) {
                    commentLines++;
                } else if (line.startsWith("/*")) {
                    commentLines++;
                    if (!line.contains("*/")) {
                        inBlockComment = true;
                    }
                } else if (line.startsWith("*")) {
                    // JavaDoc风格注释行
                    commentLines++;
                } else {
                    // 检查行内注释
                    int commentIndex = line.indexOf("//");
                    if (commentIndex >= 0) {
                        // 行内有注释
                        String codePart = line.substring(0, commentIndex).trim();
                        if (!codePart.isEmpty()) {
                            codeLines++;
                        } else {
                            commentLines++;
                        }
                    } else {
                        // 纯代码行
                        codeLines++;
                    }
                }
            }
        } catch (IOException e) {
            System.err.println("读取文件失败: " + file.getPath() + ", " + e.getMessage());
        }

        counts.put("total", totalLines);
        counts.put("code", codeLines);
        counts.put("comment", commentLines);
        counts.put("blank", blankLines);

        return counts;
    }

    /**
     * 获取文件扩展名
     */
    private static String getFileExtension(String fileName) {
        int dotIndex = fileName.lastIndexOf('.');
        if (dotIndex == -1) {
            return "";
        }
        return fileName.substring(dotIndex);
    }

    /**
     * 检查是否是目标文件类型
     */
    private static boolean isTargetFileType(String fileType) {
        for (String type : FILE_TYPES) {
            if (type.equalsIgnoreCase(fileType)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 检查是否是需要排除的目录
     */
    private static boolean shouldExclude(String dirName) {
        for (String excludeDir : EXCLUDE_FOLDERS) {
            if (excludeDir.equalsIgnoreCase(dirName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 使用示例
     */
    public static void main(String[] args) {
        // 获取项目根路径
        String projectRoot = System.getProperty("user.dir");
        // 统计并打印结果
        String report = countCodeLinesByModule(projectRoot);
        System.out.println(report);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

It-小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值