效果
代码统计报告(按模块):
文件总数: 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);
}
}