import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class CodeLineCount { /** * 只统计这个集合约定的文件名后缀的代码量, 其他后缀全部视为无效文件 -- it up to you */ private static final Set<String> VALID_SUFFIX_SET = new HashSet<>(); static { VALID_SUFFIX_SET.add(".c"); VALID_SUFFIX_SET.add(".h"); //.java文件为需要统计代码的文件 VALID_SUFFIX_SET.add(".java"); //.xml文件为需要统计代码的文件 // VALID_SUFFIX_SET.add(".xml"); //.properties文件为需要统计代码的文件 // VALID_SUFFIX_SET.add(".properties"); } /** * 需要过滤掉的目录, 例如测试用例代码所在目录 */ private static final Set<String> EXCLUDE_DIRECTORY_SET = new HashSet<>(); static { // 过滤掉/src/test目录下的文件(即测试用例相关代码) EXCLUDE_DIRECTORY_SET.add("src"+File.separator+"test"); EXCLUDE_DIRECTORY_SET.add("target"); } /** * 是否需要输出文件或者目录过滤与否的信息, 如果需要输出, 还会输出文件或者目标被过滤的原因: 无效文件or无效目录 */ private static final boolean FILE_FILTER_INFO = true; /** * 是否过滤掉源码中的注释 */ private static final boolean FILTER_COMMENT = true; public static void main(String[] args) throws Exception{ String target ; List<String> projectNameList = new ArrayList<>(); projectNameList.add("international"); for(String projectName:projectNameList) { target = "D:\ccii3\ccii\" + projectName; System.out.println(target + "(" + projectName + ")总行数:" + lineCount(target)); } } /** * 统计文件或者目录下所有文件总行数 * @param target 文件路径或者目录路径 * @return 文件或者目录下所有文件总行数 * @throws Exception */ private static int lineCount(String target) throws Exception{ // 先判断是文件还是目录 File origin = new File(target); if (origin.isFile()){ return fileCount(origin); }else{ // 如果是目录要遍历 return directoryCount(origin); } } private static int directoryCount(File dir) throws Exception { int sum = 0; File[] files = dir.listFiles(); if (files==null){ throw new IllegalArgumentException("无效的目录:"+dir.getCanonicalPath()); } for (File file:files){ if (file.isFile()){ sum += fileCount(file); }else{ sum += directoryCount(file); } } return sum; } /** * 全路径指定的文件所在目录是否是需要exclude的目录 * @param path 文件的全路径 * @return 文件所在目录是否是需要exclude的目录 */ private static boolean invalidDirectory(String path){ for (String dir: EXCLUDE_DIRECTORY_SET){ if (path.contains(dir)){ return true; } } return false; } /** * 全路径指定的文件后缀是否是约定的合法文件后缀(VALID_SUFFIX_SET指定) * @param path 文件的全路径 * @return 是否是有效的文件 */ private static boolean validFile(String path){ for (String suffix: VALID_SUFFIX_SET){ if (path.endsWith(suffix)){ return true; } } return false; } /** * 文件中内容行数统计 * @param file 文件 * @return 文件中内容总行数 * @throws Exception */ private static int fileCount(File file) throws Exception { String path = file.getCanonicalPath(); boolean validFile = validFile(path); boolean invalidDirectory = invalidDirectory(path); if (!validFile || invalidDirectory){ if (FILE_FILTER_INFO) { System.out.println("需要过滤的文件: " + path + ", " +(validFile?"无效目录":"无效文件")); } return 0; } if (FILE_FILTER_INFO) { System.out.println("不需过滤的文件: " + path); } BufferedReader reader = new BufferedReader(new FileReader(file)); int count = 0; String line; // readLine结果为null表示读到文件末尾了 while((line = reader.readLine())!=null){ // 行内容不为空的话, 如果需要过滤注释且不是注释代码行才自增, 如果不需要过滤注释代码行也自增; if (FILTER_COMMENT) { if (!isCommentLine(line)) { ++count; } }else{ // ++count; } } return count; } /** * 判断这一行内容是否是注释: 以/或者*开头就认为是注释 * @param line 一行内容 * @return 是注释则返回true, 否则返回false */ private static boolean isCommentLine(String line){ String content = line.trim(); if (content.startsWith("/") || content.startsWith("*")){ System.out.println("这一行内容是注释: "+line); return true; } return false; } }