首页 > 技术文章 > 统计指定目录下Java源代码的总行数

kinglearnjava 2015-01-22 13:17 原文

遍历指定文件夹并统计代码行数,和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;
 }

}


版权声明:本文为博主原创文章,未经博主允许不得转载。

推荐阅读