在检查项目的代码时,就想想有没有可以统计代码行数的小工具类。就上度娘查,专业的感觉比较麻烦,代码写的还是找到了。
看了他的代码,并且考下来跑了一下,挺好的,能用。
但是只能统计java代码,就在想能不能统计别的语言的代码呢?经过几个版本的代码修改,中间的代码太烂就不放出来了,嘿嘿。
最终代码,自己感觉比开始的好点了,就是多线程暂时没考虑,如果后期有时间就试着弄一下吧。也可能后期会忘了,Σ( ° △ °|||)︴。
中间java菜单处理类没接触过,就在网上找了点资料,附上参考的文章路径:
java文件对话框的使用(包括文件多选,文件、文件夹同时可选操作)
现在的代码如下。
主程序:
package utils;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
/**
* 代码扫描分析小工具
*
* @author WinSonZhao()
* @version v1.0
* @Date 2018年9月5日 上午8:40:40
* @Create eclipse Mars.2 Release (4.5.2)
* @source https://www.aliyun.com/jiaocheng/330673.html
* @source https://blog.csdn.net/maozhennba3/article/details/50359857
* @source https://www.cnblogs.com/ning1121/p/3738065.html?utm_source=tuicool&utm_medium=referral
*/
public class CodeCounterFrame extends JFrame {
private static final long serialVersionUID = 1L;
/**用于制作菜单栏时用到的一个组件
*/
JMenuBar menuBar = new JMenuBar();
/**菜单-给窗口添加菜单条
*/
JMenu fileMenu = new JMenu("文件");
/**菜单项-打开一个文件
*/
JMenuItem openMenuItem = new JMenuItem("打开");
JMenuItem openMenuFileItem = new JMenuItem("打开指定目录");
JMenuItem cleanItem = new JMenuItem("清空");
JMenuItem openMenuItems = new JMenuItem("打开多个");
/**文本域-编辑多行的文本(\r\n 换行)(\t 制表符)
*/
JTextArea txa = new JTextArea();
/**视图容器
*/
JScrollPane jsp = new JScrollPane(txa);
/**记录输出的字符串
*/
protected StringBuffer out=new StringBuffer("");
public CodeCounterFrame() {
this.setJMenuBar(this.menuBar);
//添加菜单条
this.menuBar.add(fileMenu);
//添加菜单项
this.fileMenu.add(this.openMenuItem);
this.fileMenu.add(this.cleanItem);
this.fileMenu.add(this.openMenuFileItem);
this.fileMenu.add(this.openMenuItems);
// txa里面的内容不能被编辑
this.txa.setEditable(false);
add(this.jsp);
// 内部匿名类监听器
this.openMenuItem.addActionListener(new ActionListener() {
// 点击了打开菜单
public void actionPerformed(ActionEvent e) {
if (e.getSource() == openMenuItem) {
init_1(null);
}
}
});
this.cleanItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(e.getSource() == cleanItem){
txa.setText("");
clear();
}
}
});
this.openMenuFileItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(e.getSource() == openMenuFileItem){
init_1("D:\\0Working\\workspace\\");
}
}
});
this.openMenuItems.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(e.getSource() == openMenuItems){//指定目录
init_2("D:\\0Working\\");
}
}
});
}
/**
* 清空
* @param br
*/
public void clear() {
if (this.out != null && this.out.length() > 0) {
// 清空
this.out.delete(0, out.length() - 1);
}
}
public void init_2(String fName){
JFileChooser chooser = new JFileChooser();// 弹出文件选择器
if(fName!=null&&!"".equals(fName.trim())){
chooser.setCurrentDirectory(new File(fName));//打开指定目录
}
//设置文件选择器,以允许选择多个文件
chooser.setMultiSelectionEnabled(true);
//弹出一个带标题的 "Open File" 文件选择器对话框
chooser.showOpenDialog(null);
//可以选择文件和目录
// chooser.setFileSelectionMode(chooser.FILES_AND_DIRECTORIES);
// 获取选中的文件
File[] selectedFiles = chooser.getSelectedFiles();
if(selectedFiles==null||selectedFiles.length<=0){
txa.setText("使用‘打开多个’菜单,未选择文件");
return;
}
CodeFacetory cf=new CodeFacetory();
for(File f:selectedFiles){
out.append(cf.scanFile(f));
}
txa.setText(out.toString());
}
//选择‘打开’菜单,选择一个文件
public void init_1(String fName){
JFileChooser chooser = new JFileChooser();
if(fName!=null&&!"".equals(fName.trim())){
chooser.setCurrentDirectory(new File(fName));
//弹出一个带标题的 "Open File" 文件选择器对话框
chooser.showDialog(null,"指定目录");
}else{
//弹出一个 "Open File" 文件选择器对话框
//等同于:showOpenDialog(null)
chooser.showDialog(null,"默认目录");
}
// 获取选中的文件
File selectedFile = chooser.getSelectedFile();
if(selectedFile==null){
txa.setText("使用‘打开’菜单,未选择文件");
return;
}
CodeFacetory cf=new CodeFacetory();
out.append(cf.scanFile(selectedFile));
txa.setText(out.toString());
}
// Frame主程序
public static void main(String[] args) {
try {// windows界面风格
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
CodeCounterFrame frame = new CodeCounterFrame();
frame.setTitle("统计代码的小工具");
frame.setSize(600, 400);
frame.setVisible(true);
frame.setLocationRelativeTo(null);// 窗体居中显示
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
根据文件类型处理的工厂类
package utils;
import java.io.BufferedReader;
import java.io.File;
/**
* 处理文件工厂类
* @author WinSonZhao()
* @version v1.0
* @Date 2018年9月5日 下午5:45:59
* @Create eclipse Mars.2 Release (4.5.2)
*/
public class CodeFacetory {
public String scanFile(File... file){
BufferedReader br = null;
CodeFace cf=null;
if(file==null||file.length<=0){
return "未选择文件";
}
String name="";
StringBuilder sb=new StringBuilder("");
for(File f:file){
name=f.getName();
if(name.endsWith(".java")){
cf=new CodeJava();
}else if(name.endsWith(".xml")){
cf=new CodeXml();
}else{
return name+"【文件格式不能解析】\r\n";
}
cf.start(f,br);
sb.append(name).append("\t");
sb.append(cf.to_StringBuilder()).append("\r\n");
}
return sb.toString();
}
}
定义的接口,方便工厂类中使用
package utils;
import java.io.BufferedReader;
import java.io.File;
/**
* 接口
* @author WinSonZhao()
* @version v1.0
* @Date 2018年9月5日 下午5:19:08
* @Create eclipse Mars.2 Release (4.5.2)
*/
public interface CodeFace {
/**
* 工具入口
* @param file
* @param br
* @return
*/
public void start(File file,BufferedReader br);
/**
* 组装统计文本
* @return
*/
public String to_String();
/**
* 组装统计文本
* @return
*/
public StringBuilder to_StringBuilder();
/**
* 清空
* @param br
*/
public void clear();
}
定义的特定格式文件解析父类(名称暂时就叫这个吧,起名真费劲……)
package utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* 代码扫描父类
*
* @author WinSonZhao()
* @version v1.0
* @Date 2018年9月6日 上午8:51:16
* @Create eclipse Mars.2 Release (4.5.2)
* @see 当前版本,只是考虑功能实现,暂时没考虑多线程并发的共享资源的问题,后期有时间再弄吧(也可能是无期)
*/
public class CodeCounter implements CodeFace {
/**
* 记录输出的字符串
*/
protected StringBuilder out = new StringBuilder("");
/**
* 装载从缓存中读取的数据
*/
protected String line = "";
/**
* 缓存区-装载读文件的缓存
*/
protected BufferedReader br = null;
/**
* 代码行数
*/
protected static long codeLines = 0;
/**
* 注释行数
*/
protected static long commentLines = 0;
/**
* 空白行数
*/
protected static long blankLines = 0;
public CodeCounter() {
clear();// 清空
}
public CodeCounter init(BufferedReader br) {
this.br = br;
return this;
}
public CodeCounter init(StringBuilder out) {
this.out = out;
return this;
}
/**
* 工具入口
*
* @param file
* @param br
* @return
*/
public void start(File file, BufferedReader br) {
if (this.out == null) {
this.out = new StringBuilder();
}
try {
this.buffer(file, br);
} catch (FileNotFoundException ex) {
throw new CodeException("File Is Not Found...."+file);
} catch (IOException ex) {
throw new CodeException("BufferedReader Io Exception...."+br);
} catch (Exception ex) {
throw new CodeException(ex);
} finally {
this.close(br);
}
}
/**
* 清空
*
* @param br
*/
public void clear() {
if (this.out != null && this.out.length() > 0) {
// 清空
this.out.delete(0, out.length() - 1);
}
}
/**
* 工具入口
*
* @param file
* @param br
* @return
* @throws IOException
*/
protected void buffer(File file, BufferedReader br) throws IOException {
// 读取文档的内容
FileReader reader = new FileReader(file);
// 读文件,缓存区
br = new BufferedReader(reader);
String name = file.getName();
out.append(name).append("\t");
this.scan(br);
clear();// 清空
}
/**
* 核心代码-扫描java文件内容,统计代码
*
* @param br
* @throws IOException
*/
protected void scan(BufferedReader br) throws IOException {
boolean comment = false;
// 文件内容提取,按行读取
while ((line = br.readLine()) != null) {
// 除去注释前的空格
line = line.trim();
if (line.matches("^[ ]*$")) {
blankLines++;// 匹配空行
} else if (line.startsWith("//")) {
commentLines++;// 单行注释
} else if (line.startsWith("/*") && line.endsWith("*/")) {
commentLines++;// 多行注释,只有一行的情况
} else if (line.startsWith("/*") && !line.endsWith("*/")) {
commentLines++;// 多行注释,开始
comment = true;
} else if (comment == true) {
commentLines++;
// 多行注释,结束
if (line.endsWith("*/")) {
comment = false;
}
} else {// 其他的都算为有效代码
codeLines++;
}
}
}
/**
* 关闭缓存,并清空
*
* @param br
*/
protected void close(BufferedReader br) {
if (br != null) {
try {
br.close();
br = null;
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
/**
* 统计总行数
*
* @return
*/
public long sum() {
return codeLines + commentLines + blankLines;
}
/**
* 组装基础的统计数据
*/
protected void to() {
out.append("代码行数:").append(codeLines).append("\t");
out.append("注释行数:").append(commentLines).append("\t");
out.append("空白行数:").append(blankLines).append("\t");
}
/**
* 组装统计文本
*
* @return
*/
public String to_String() {
to();
out.append("总行数:").append(sum());
return out.toString();
}
/**
* 组装统计文本
*
* @return
*/
public StringBuilder to_StringBuilder() {
to();
out.append("总行数:").append(sum());
return out;
}
}
java文件的处理,这个是将父类完全覆盖,可以按需要定制解析
package utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* 统计java文件的类
* @author WinSonZhao()
* @version v1.0
* @Date 2018年9月5日 下午4:33:59
* @Create eclipse Mars.2 Release (4.5.2)
*/
public class CodeJava extends CodeCounter implements CodeFace{
/**记录输出的字符串
*/
protected StringBuilder out=new StringBuilder("");
/**装载从缓存中读取的数据
*/
protected String line = "";
/**缓存区-装载读文件的缓存
*/
protected BufferedReader br=null;
/**代码行数
*/
static long codeLines = 0;
/**注释行数
*/
static long commentLines = 0;
/**空白行数
*/
static long blankLines = 0;
public CodeJava(){
clear();
}
public CodeJava init(BufferedReader br){
this.br=br;
return this;
}
public CodeJava init(StringBuilder out){
this.out=out;
return this;
}
public String to_String() {
to();
out.append("总行数:").append(sum());
return out.toString();
}
public StringBuilder to_StringBuilder() {
to();
out.append("总行数:").append(sum());
return out;
}
public void start(File file, BufferedReader br) {
if (this.out == null) {
this.out = new StringBuilder();
}
try {
this.buffer(file, br);
} catch (FileNotFoundException ex) {
throw new CodeException("File Is Not Found...."+file);
} catch (IOException ex) {
throw new CodeException("BufferedReader Io Exception...."+br);
} catch (Exception ex) {
throw new CodeException(ex);
} finally {
this.close(br);
}
}
protected void to(){
out.append("代码行数:").append(codeLines).append("\t");
out.append("注释行数:").append(commentLines).append("\t");
out.append("空白行数:").append(blankLines).append("\t");
}
/**
* 清空
*/
public void clear() {
if(this.out!=null&&this.out.length()>0){
this.out.delete(0, out.length()-1);//清空
}
}
/**
* 统计总行数
*/
public long sum(){
return codeLines + commentLines + blankLines;
}
/**
* 工具入口
*/
protected void buffer(File file,BufferedReader br) throws IOException{
FileReader reader = new FileReader(file);// 读取文档的内容
br = new BufferedReader(reader);//读文件,缓存区
String name=file.getName();
out.append(name).append("\t");
this.scan(br);//
this.clear();//清空
}
/**
* 核心代码-扫描java文件内容,统计代码
*/
protected void scan(BufferedReader br) throws IOException{
boolean comment = false;
while ((line = br.readLine()) != null) {// 文件内容提取,按行读取
line = line.trim(); // 除去注释前的空格
if (line.matches("^[ ]*$")) {
blankLines++;// 匹配空行
} else if (line.startsWith("//")) {
commentLines++;//单行注释
} else if (line.startsWith("/*") && line.endsWith("*/")) {
commentLines++;//多行注释,只有一行的情况
} else if (line.startsWith("/*") && !line.endsWith("*/")) {
commentLines++;//多行注释,开始
comment = true;
} else if (comment == true) {
commentLines++;
if (line.endsWith("*/")) {//多行注释,结束
comment = false;
}
} else {//其他的都算为有效代码
codeLines++;
}
}
}
/**
* 关闭缓存,并清空
* @param br
*/
protected void close(BufferedReader br){
if (br != null) {
try {
br.close();
br = null;
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
xml文件解析,如果只是核心代码分析不一样,可以很简单实现
package utils;
import java.io.BufferedReader;
import java.io.IOException;
/**
* @author WinSonZhao()
* @version v1.0
* @Date 2018年9月5日 下午5:18:41
* @Create eclipse Mars.2 Release (4.5.2)
*/
public class CodeXml extends CodeCounter implements CodeFace{
protected void scan(BufferedReader br) throws IOException{
boolean comment = false;
while ((line = br.readLine()) != null) {// 文件内容提取,按行读取
line = line.trim(); // 除去注释前的空格
if (line.matches("^[ ]*$")) {
blankLines++;// 匹配空行
} else if (line.startsWith("<!--") && line.endsWith("-->")) {
commentLines++;//多行注释,只有一行的情况
} else if (line.startsWith("<!--") && !line.endsWith("-->")) {
commentLines++;//多行注释,开始
comment = true;
} else if (comment == true) {
commentLines++;
if (line.endsWith("-->")) {//多行注释,结束
comment = false;
}
} else {//其他的都算为有效代码
codeLines++;
}
}
}
}
发布后,发现没有异常类,补上,虽然也没啥。测试统计中,分析了这类,可以数一下,o(∩_∩)o 哈哈
package utils;
/**
* 自定义异常
* @author WinSonZhao(***)
* @version v1.0
* @Date 2018年9月6日 上午9:16:43
* @Create eclipse Mars.2 Release (4.5.2)
*/
public class CodeException extends RuntimeException {
private static final long serialVersionUID = 1L;
//构造
public CodeException() {
super("CodeException:Something is really wrong! hi hi..");
}
/*构造*/
public CodeException(String message) {
super("CodeException:"+message);
}
/*构
* 造*/
public CodeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public CodeException(String message, Throwable cause) {
super(message, cause);
}
public CodeException(Throwable cause) {
super(cause);
}
}
下面就是一些截图了
配置文件就不上传了
我还扩展了jsp和js 的分析,就不传了,jsp就做了简单的统计,没有做细致的分析。
因为这个代码只写了一天,中间改了好多次。还是比较简陋,望各位大佬多多指教呀,小弟在此谢过啦……
就这样吧,( ^_^ )/~~拜拜