遍历指定文件夹并统计代码行数,和Java关键字频率,降序输出所有结果。
默认统计.java文件,可以自定义统计其他源码。
打算将它整合到自己做的桌面小程序中。
部分输出如下:
代码总行数:11341 E:\java\以往习作\KingTetris.java--->行数:419 F:\EclipseProject\Test\src\king\countcode\CountCodeLine.java--->行数:340 F:\EclipseProject\记事本\src\king\notepad\view\NotepadFrame.java--->行数:332 F:\EclipseProject\五子棋\src\guyuanguanli.java--->行数:298 F:\EclipseProject\记事本\src\king\notepad\service\TextService.java--->行数:277 F:\EclipseProject\简易绘图板\src\King\Test.java--->行数:267
---------Java代码关键字统计:---------- 关键字:new 出现次数:873 关键字:int 出现次数:668 关键字:public 出现次数:608 关键字:private 出现次数:520 关键字:null 出现次数:511 关键字:void 出现次数:413 关键字:static 出现次数:299 关键字:return 出现次数:293 关键字:if 出现次数:282 关键字:this 出现次数:232 关键字:class 出现次数:201 关键字:for 出现次数:161 关键字:true 出现次数:146 关键字:else 出现次数:92 关键字:final 出现次数:88 关键字:false 出现次数:79 关键字:case 出现次数:78 关键字:catch 出现次数:68 关键字:while 出现次数:67 关键字:try 出现次数:63 关键字:boolean 出现次数:60 关键字:extends 出现次数:56 关键字:implements 出现次数:55 关键字:throws 出现次数:45 关键字:break 出现次数:41 关键字:char 出现次数:40 关键字:super 出现次数:34 关键字:byte 出现次数:32 关键字:switch 出现次数:22 关键字:double 出现次数:17 关键字:abstract 出现次数:16 关键字:do 出现次数:15 关键字:protected 出现次数:13 关键字:synchronized 出现次数:9 关键字:long 出现次数:9 关键字:finally 出现次数:8 关键字:throw 出现次数:8 关键字:instanceof 出现次数:7 关键字:default 出现次数:6 关键字:float 出现次数:2 关键字:continue 出现次数:2 关键字:package 出现次数:2 关键字:transient 出现次数:2 关键字:import 出现次数:2 关键字:interface 出现次数:2 关键字:native 出现次数:2 关键字:volatile 出现次数:2 关键字:short 出现次数:2 关键字:strictfp 出现次数:1 关键字:enum 出现次数:1 关键字:const 出现次数:1 关键字:goto 出现次数:1 关键字:assert 出现次数:1
package king.countcode; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 统计指定目录下Java文件的总行数、关键字频率 * */ public class CountCodeLine { private int count = 0; // 所有需要遍历的文件 private List<File> allFile = new LinkedList<File>(); // 所有遍历结果 private Map<String, Integer> allResult = new HashMap<String, Integer>(); // 排序后的所有遍历结果 private List<Map.Entry<String, Integer>> allSortedResult = new LinkedList<Map.Entry<String, Integer>>(); // 需要检查的文件后缀,默认添加.java private List<String> allSuffix = new LinkedList<String>(); private static final String JAVA_SUFFIX = ".java"; //提取单词的正则表达式 private Pattern p = Pattern.compile("(?<=\\W)\\w+(?=\\W)"); //用于计数 private Keywords countKeyword = new Keywords(); public CountCodeLine(){ addSuffix(JAVA_SUFFIX); } public static void main(String[] args) { CountCodeLine count = new CountCodeLine(); // 添加需要检查的文件或文件夹 count.addPath("E:\\java"); count.addPath("F:\\EclipseProject"); // // 遍历C盘之外所有硬盘 // // 由于文件太多,全部遍历需要很久才能出结果,一般不建议 // File[] roots = File.listRoots(); // for(int i = 0; i < roots.length; i++){ // // C盘某些文件夹无权限进入,会报错,故剔除 // if (!roots[i].getAbsolutePath().toLowerCase().startsWith("c:")){ // count.addPath(roots[i].getAbsolutePath()); // } // } // 检查并返回统计 总行数 System.out.println("代码总行数:" + count.execute()); // 输出降序排列的总结果 for(Map.Entry<String, Integer> entry : count.getAllSortedResult()){ System.out.println(entry.getKey() + "--->行数:" + entry.getValue()); } //输出所有Java文件中的关键字频率统计 // 如果想统计单个文件,请在上方addPath()仅输入单个文件的绝对地址 System.out.println(); System.out.println("---------Java代码关键字统计:----------"); System.out.println(count.getUsedKeywordFrequency()); } /* * 添加需要检查的文件或文件夹 */ public void addPath(String path){ allFile.add(new File(path)); } /* * 添加需要检查的后缀名 * 如果要用来统计其他后缀名的文件,请先clear() */ public void addSuffix(String suffix){ suffix = suffix.toLowerCase(); //全转小写 if (!suffix.startsWith(".")) suffix = "." + suffix; allSuffix.add(suffix); } /* * 开始遍历 */ public int execute(){ for(File file : allFile){ checkFile(file); } // 排序所有结果 sortAllResult(); return getCount(); } /* * 获取当前count值 */ public int getCount(){ return count; } /* * 返回详细的结果 * 文件名-代码行数映射的Set<Map.Entry<String, Integer>> */ public Set<Map.Entry<String, Integer>> getAllResult(){ return allResult.entrySet(); } /** * 返回所有排序后的结果 */ public List<Map.Entry<String, Integer>> getAllSortedResult(){ return new LinkedList<Map.Entry<String, Integer>>(allSortedResult); } /** * 返回关键字频率的统计结果 * @return */ public String getUsedKeywordFrequency(){ return countKeyword.getUsedKeywordFrequency(); } /* * 清空所有内容,以便开始全新的查找 */ public void clear(){ count = 0; allFile.clear(); allResult.clear(); allSortedResult.clear(); allSuffix.clear(); } /* * 遍历文件夹 */ private void checkFile(File file){ if (file.isDirectory()){ File[] files = file.listFiles(); for(File f : files){ checkFile(f); } } else if (checkSuffix(file)){ countLine(file); } // // 下面是循环方法,当递归溢出时,可改用循环 // Stack<File> stack = new Stack<File>(); // stack.push(file); // while(!stack.isEmpty()){ // File tmp = stack.pop(); // if (tmp.isDirectory()){ // File[] files = tmp.listFiles(); // for (File f : files){ // stack.push(f); // } // } else if (checkSuffix(tmp)){ // countLine(tmp); // } // } } /** * 检查后缀名 */ private boolean checkSuffix(File file){ String fileName = file.getName().toLowerCase(); for(String suffix : allSuffix){ if (fileName.endsWith(suffix)) return true; } return false; } /* * 统计Java文件的行数 */ private void countLine(File file){ try (BufferedReader input = new BufferedReader(new FileReader(file))){ String str = null; int tmpCount = 0; while ((str = input.readLine()) != null){ // 检查这一行里的每一个单词 Matcher m = p.matcher(str); while(m.find()){ // 判断是否Java关键字 countKeyword.checkWord(m.group()); } tmpCount++; } count += tmpCount; allResult.put(file.getAbsolutePath(), tmpCount); //保存到集合中 } catch (IOException e) { e.printStackTrace(); } } /** * 排序所有统计结果 * 降序 */ private void sortAllResult(){ allSortedResult.addAll(allResult.entrySet()); Collections.sort(allSortedResult, new Comparator<Map.Entry<String, Integer>>(){ @Override public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2){ int value1 = o1.getValue(); int value2 = o2.getValue(); if (value1 == value2) return 0; return value1 > value2 ? -1 : 1; } }); } } //关键字集合,用于计数 class Keywords { // 所有关键字 private Map<String, Integer> keywords = new HashMap<String, Integer>(); //构造器 public Keywords(){ reset(); } //复位清零 public void reset(){ for(Keyword k : Keyword.values()){ //从枚举类获取关键字 keywords.put(k.getKeyword(), 0); } } //检测一个单词是否关键字,如果是,就让对应的关键字频数加1 public void checkWord(String word){ if (keywords.keySet().contains(word)){ keywords.put(word, keywords.get(word) + 1); } } //返回统计结果 public String getUsedKeywordFrequency(){ StringBuilder result = new StringBuilder(); StringBuilder tmp = new StringBuilder(); //按频数即value值排序 //就算用TreeMap也只能对key排序,下面用自己写的比较器排序 //继承Comparator接口改改名compare方法后,TreeSet会认为compare结果为0的两个元素是同一个元素,故不用TreeSet List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(keywords.entrySet()); Collections.sort(list, new MyMapEntryComparator()); for(Map.Entry<String, Integer> entry : list){ if (entry.getValue() != 0){ tmp.setLength(0); tmp.append("关键字:").append(entry.getKey()).append("出现次数:").append(entry.getValue()); result.append(typeset(tmp)).append("\n"); } } return result.toString(); } /** * 将字符串排版再返回,如下输出不整齐 * 关键字:null 出现次数:1 * 关键字:implements 出现次数:1 * 控制“关键字”和“出现次数”之间的空格, * 将来可能改变输出的汉字,故下面用正则式考虑更广泛的情况 * @param StringBuilder * @return String */ private String typeset(StringBuilder sb){ StringBuilder result = new StringBuilder(); int blank = 15; //不同组汉字之间的空白 int index = 0, length = 0, differ; //找出右边不是紧邻汉字的汉字,向后数十五个空 Pattern p = Pattern.compile("(?<![一-龥])[一-龥]+(?![一-龥])"); Matcher m = p.matcher(sb); while(m.find()){ //find()后移一位 result.append(sb.substring(index, m.start())); if((differ = blank -(m.start() - index - length)) > 0 && m.start() != 0){ while(differ-- > 0){ result.append(" "); } } index = m.start(); length = m.group().length(); } //如果后面中文,则把剩下的全接上去 result.append(sb.substring(index)); return result.toString(); } //比较用的Comparator,只在这时用到,故写成内部类 private class MyMapEntryComparator implements Comparator<Map.Entry<String, Integer>>{ @Override public int compare(Map.Entry<String, Integer> m1, Map.Entry<String, Integer> m2){ return -(m1.getValue() - m2.getValue()); } } } //所有关键字,枚举类,仅用于存储 enum Keyword{ ABSTRACT("abstract"), ASSERT("assert"), BOOLEAN("boolean"), BREAK("break") , BYTE("byte"), CASE("case"), CATCH("catch"), CHAR("char"), CLASS("class") , CONST("const"), CONTINUE("continue"), DEFAULT("default"), DO("do"), DOUBLE("double") , ELSE("else"), ENUM("enum"), EXTENDS("extends"), FALSE("false"), FINAL("final") , FINALLY("finally"), FLOAT("float"), FOR("for"), GOTO("goto"), IF("if") , IMPLEMENTS("implements"), IMPORT("import"), INSTANCEOF("instanceof"), INT("int") , INTERFACE("interface"), LONG("long"), NATIVE("native"), NEW("new"), NULL("null") , PACKAGE("package"), PRIVATE("private"), PROTECTED("protected"), PUBLIC("public") , RETURN("return"), SHORT("short"), STATIC("static"), STRICTFP("strictfp") , SUPER("super"), SWITCH("switch"), SYNCHRONIZED("synchronized"), THIS("this") , THROW("throw"), THROWS("throws"), TRANSIENT("transient"), TRY("try"), TRUE("true") , VOID("void"), VOLATILE("volatile"), WHILE("while"); private String keyword; //构造器 private Keyword(String keyword){ this.keyword = keyword; } //获取keyword public String getKeyword() { return keyword; } }