SCAU综合性实验 Java 源代码注释及关键字分析程序 (附类图)
题目:
类别: D 综合性实验
关键字: 类、对象、封装、聚合、组合、继承、IO
内容要求:
一、实验目的
(1)掌握面向对象程序设计的基本方法
(2)应用 Java 语言编写应用程序
二、实验内容 编写一个 Java 应用程序,对单个 Java 源程序文件及某个目录中的所有 Java 源程序文件(包括子目录)进 行分析,分析内容包括:
Java 源程序文件个数,对目录分析进行分析时需要。
Java 源程序中的字符个数,对目录分析时是其中所有源程序文件的字符个数总和。
Java 源程序文件中的注释的个数,即源程序文件中共有多少个注释,包括:单行注释和多行注释。对目录 分析时是其中所有源程序文件的总和。
Java 源程序文件中的注释的字符个数,即源程序文件中所有注释的字符数之和。对目录分析时是其中所有 源程序文件的总和。
Java 源程序文件中关键字使用情况,即源程序文件各个关键字使用了多少次。对目录分析时是其中所有源 程序文件的总和。
具体要求如下:
1.程序运行首先显示如下图所示的菜单:
2.分析目录或源程序文件要求
选择菜单项目 1 时,首先要求输入要分析的目录名或 Java 源程序文件名。 如果输入的目录或文件名不存在,提示不存在;输入的文件名的扩展名不是“.java”时提示不是 Java 源 程序文件。 如果输入的是一个 Java 源程序文件名,对该源程序文件进行分析。 如果输入的是一个目录名,对该目录中所有的源程序文件进行分析。 分析的结果存储到一个文本文件中,在当前目录中建立一个 data 目录,结果文件放在 data 目录中。 分析目录时结果文件名:D_目录名_Result.txt,例如:D_lang_Result.txt 分析源程序文件时结果文件名:F_源程序文件名_Result.txt,
例如:F_String.java_Result.txt 结果文件中内容的格式:
第 1 行:分析目录 : C:\Program Files\Java\jdk1.8.0_31\src\java
第 2 行:空行
第 3 行:Java 源程序文件个数: 1866 (分析文件时无此行)
第 4 行:源程序中字符总个数 : 29022541
第 5 行:注释总个数 : 57349
第 6 行:注释总的字符数 : 17559371
第 7 行:空行
第 8 行:关键字使用情况如下:
第 9 行:[int = 27705] (从第 9 行开始输出各个关键字及其使用的次数,每行一个)
说明: 分析结束时,不显示分析结果,结果存储到文本文件,显示如下提示: 目录分析结束, 分析结果存放在文件[data/D_util_Result.txt]! 或者: 文件分析结束, 分析结果存放在文件[data/F_String.java_Result.txt]! 关键字输出时,按使用次数从大到小排序,次数相同时,按字母顺序排序。
// Java 语言的所有关键字 public static final String[] KEYWORDS = { “abstract”, “assert”, “boolean”, “break”, “byte”, “case”, “catch”, “char”, “class”, “const”, “continue”, “default”, “do”, “double”, “else”, “enum”, “extends”, “final”, “finally”, “float”, “for”, “goto”, “if”, “implements”, “import”, “instanceof”, “int”, “interface”, “long”, “native”, “new”, “package”, “private”, “protected”, “public”, “return”, “short”, “static”, “strictfp”, “super”, “switch”, “synchronized”, “this”, “throw”, “throws”, “transient”, “try”, “void”, “volatile”, “while” };
2.查看已有的分析结果要求 选择菜单项目 2 时,首先列出已经分析并存储的结果,如下图:
即列出 data 目录中存储的所有分析结果文件,并给出一个序号。 输入要查看的文件序号后,显示该文件中的内容。例如下图:
三、实验完成提示
1.分析注释个数和注释的字符数时,假设没有注释嵌套的问题。即测试用的文件和目录中没有如下情况: /** //注释 1 / // 注释 2 / */ *
*2.分析注释个数和注释的字符数时,假设字符串直接量中没有注释形式,即没有下面的情况: String s = “/abcd/”;
3.分析关键字使用次数时,注意以下几种情况不能计算关键字个数:
(1) 注释中出现的关键字,例如下面的 int 不能计数 /** * int k=0; */
(2) 字符串直接量中的关键字,例如下面的 int 不能计数 System.out.println(“input a int: ”);
(3) 注意整字识别,例如 println 中的 int 不是关键字,不能计数。
4.如果使用正则表达式进行编程,除基本的正则表达式使用外,可以参考 Java 的如下两个类: java.util.regex.Pattern java.util.regex.Matcher
代码:这里分了五个类以及一个接口
第一个类:Main类(主函数)
作用:主函数,可以调用其它的功能(具体见注释)
代码:
package finalExam;
import java.io.*;
import java.util.Scanner;
public class Main {
private static String addressList;//用于存储输入的路径
public static String getAddressList() {
return addressList;
}
public static void setAddressList(String addressList) {
Main.addressList = addressList;
}
//主函数
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
while (true) {//循环至退出
menu();
int choose = scanner.nextInt();
switch (choose) {//选择
case 1: {
System.out.println("请输入:");
String str = scanner.next();
AnalyseDir analyseDir = new AnalyseDir(str);//创建analyseDir对象
File f = new File(str);
if (str.contains(".java")) {
addressList = str;
analyseDir.programData();
} else if (!f.exists()) {
System.out.println("不存在此目录或文件名");
} else if (f.isDirectory()) {
addressList = str;
analyseDir.documentData();
} else System.out.println("不是 Java 源程序文件");
break;
}
case 2: {
ScanResult.menu2();
break;
}
case 0:
return;
}
// scanner.close();
}
}
public static void menu() {//主界面
System.out.println("-------MENU-------");
System.out.println(" 1.分析目录或源程序文件");
System.out.println(" 2.查看已有的分析结果");
System.out.println(" 0.退出");
System.out.println("------------------");
System.out.println("请选择:");
}
}
第二个类:AnalyseDir
作用:分析输入的路径,分别对目录名以及文件名进行分析
代码:
package finalExam;
import java.io.*;
import java.util.List;
import java.util.Objects;
public class AnalyseDir extends Main implements CorrectRoad {
static String sdir = "data";
private String str;
//有参构造
public AnalyseDir(String str) {
this.str = str;
}
//封装
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public void programData() throws IOException {//源程序文件
try (
FileReader fr = new FileReader(str);
BufferedReader bfr = new BufferedReader(fr)//用缓冲读取
) {
int sum = 0;
while (bfr.read() != -1) {//统计字符数
sum++;
}
//对路径进行处理
int i = CorrectRoad.nameWhere(str);
String str3 = str.substring(i + 1);
String str2 = str.substring(0, i);
i = CorrectRoad.nameWhere(str2);//第二个/
String str1 = str.substring(0, i+1);
File fdir = new File(str1 + sdir);
String last = "F_" + str3 + "_Result.txt";
File fdir2 = new File(str1 + sdir + "/" + last);
if (!fdir.isDirectory()) {
fdir.mkdir();
fdir2.createNewFile(); // 创建新文件
} else if (!fdir2.exists()) {
fdir2.createNewFile(); // 创建新文件
}
List list = TheKeyWord.makeMap(str);
BufferedWriter bw = new BufferedWriter(new FileWriter(fdir2));//用缓冲写入
SearchAnnotation searchAnnotation = new SearchAnnotation(str);
try {
bw.write(
"------------------------------------------------------------------\n" +
"分析目录 :" + str + "\n" +
"\n" +
"源程序中字符总个数 : " + sum + "\n" +
"注释总个数 : " + searchAnnotation.search(0) + "\n" +
"注释总的字符数 : " + searchAnnotation.search(1) + "\n" +
"\n" +
"关键字使用情况如下:\n");
for (Object o : list) {
bw.write(
"[" + o + "] \n");
}
bw.write(
"------------------------------------------------------------------");
} catch (Exception e) {
e.printStackTrace();
}
bw.flush();
System.out.println("文件分析结束, 分析结果存放在文件[data/F_" + str3 + "_Result.txt]!");
}
}
public void documentData() throws IOException {//目录
int count = 0, sum1 = 0, sum2 = 0, sum3 = 0;//用于累加
File fdir0 = new File(str);
for (int i = 0; i < Objects.requireNonNull(fdir0.list()).length; i++) {
if (Objects.requireNonNull(fdir0.list())[i].contains(".java")) {
String strAddress = str + "/" + Objects.requireNonNull(fdir0.list())[i];
SearchAnnotation searchAnnotation = new SearchAnnotation(strAddress);
try {
FileReader fr = new FileReader(strAddress);
BufferedReader br1 = new BufferedReader(fr);
while (br1.read() != -1) {//统计字符数
sum3++;
}
sum1 += searchAnnotation.search(0);//注释总个数
sum2 += searchAnnotation.search(1);//注释总的字符数
} catch (Exception e) {
e.printStackTrace();
}
count++;
}
}
//对路径进行处理
int j = CorrectRoad.nameWhere(str);
String str3 = str.substring(j + 1);
String str1 = str.substring(0, j + 1);
File fdir = new File(str1 + sdir);
String last = "D_" + str3 + "_Result.txt";
File fdir2 = new File(str1 + "/" + sdir + "/" + last);
if (!fdir.isDirectory()) {
fdir.mkdir();
fdir2.createNewFile(); // 创建新文件
} else if (!fdir2.exists()) {
fdir2.createNewFile(); // 创建新文件
}
List list = TheKeyWord.makeMap(str);
BufferedWriter bw = new BufferedWriter(new FileWriter(fdir2));//用缓冲写入
bw.write(
"------------------------------------------------------------------\n" +
"分析目录 : " + str + "\n" +
"\n" +
"Java 源程序文件个数: " + count + " \n" +
"源程序中字符总个数 : " + sum3 + "\n" +
"注释总个数 : " + sum1 + "\n" +
"注释总的字符数 : " + sum2 + "\n" +
"\n" +
"关键字使用情况如下:\n");
for (Object o : list) {
bw.write(
"[" + o + "] \n");
}
bw.write(
"------------------------------------------------------------------");
bw.flush();
System.out.println("目录分析结束, 分析结果存放在文件[data/D_" + str3 + "_Result.txt]!");
}
}
第三个类:ScanResult
作用:对主菜单中选项2的功能实现
代码:
package finalExam;
import java.io.*;
import java.util.Objects;
import java.util.Scanner;
public class ScanResult extends Main implements CorrectRoad {
public static void showResult(File f, int n) throws IOException {
String str;
try (//用缓冲读取
FileReader fr = new FileReader(f.getPath()+'/'+ Objects.requireNonNull(f.list())[n - 1]);
BufferedReader bfr = new BufferedReader(fr)
) {
while ((str = bfr.readLine()) != null) {//输出文件内容
System.out.println(str);
}
}
}
public static void menu2() throws IOException {//查看已有的分析结果界面
String str;
String str0 = getAddressList();
if(str0.contains(".java")){
int i = CorrectRoad.nameWhere(str0);
String str2 = str0.substring(0, i);
i = CorrectRoad.nameWhere(str2);
String str3 = str2.substring(0, i);
str = str3 + "/data";
}else{
String str1 = str0.substring(0, CorrectRoad.nameWhere(str0));//调整为可读路径
str = str1 + "/data";
}
Scanner scanner = new Scanner(System.in);
File f = new File(str);
System.out.println("--------------------------------------------");
for (int i = 1; i <= Objects.requireNonNull(f.list()).length && Objects.requireNonNull(f.list())[i - 1] != null; i++) {
System.out.println(" " + i + " -- " + Objects.requireNonNull(f.list())[i - 1]);
}
System.out.println("--------------------------------------------");
System.out.println("输入要查看的文件的编号:");
int choose2 = scanner.nextInt();
showResult(f,choose2);
}
}
第四个类:SearchAnnotation
这里是唯一一个参考其它大佬的代码,因为自己敲了很久,还是敲不出来。
作用:寻找注释
代码:
package finalExam;
import java.io.*;
import java.util.ArrayList;
public class SearchAnnotation extends AnalyseDir {
//有参构造
public SearchAnnotation(String str) {
super(str);
}
public int search(int n) throws Exception {
ArrayList<String> ve = new ArrayList<>();
try (//用缓冲读取
FileReader fr = new FileReader(super.getStr());
BufferedReader bfr = new BufferedReader(fr)
) {
String s;
while ((s = bfr.readLine()) != null) {//使用readLine方法,一次读一行
ve.add(s);
}
}
if (n == 0) return operateNote(ve)[0];
else return operateNote(ve)[1];
}
public static int[] operateNote(ArrayList<String> list) throws IOException {
String s;
int countNote = 0;
int charInNote = 0;
for (int j = 0; j < list.size(); j++) {
s = list.get(j);
int note1 = s.indexOf("/*");
int note2 = s.indexOf("//");
int note3 = s.indexOf("*/");
//int note4=s.indexOf("\"");
String dm = "\"(.*)\"";//双引号
String sm = "\'(.*)\'";//单引号
if (note1 != -1 && note3 == -1) {//多行注释
countNote++;
String ttt = list.get(j);
list.set(j, ttt.substring(0, note1));
charInNote += s.substring(note1).length() + 1;//+1是包括换行符
s = list.get(++j);
while ((note3 = s.indexOf("*/")) == -1) {
if ((note2 = s.indexOf("//")) != -1) {
countNote++;
}
list.remove(j);
charInNote += s.length() + 1;
if (j < list.size() - 1) {
s = list.get(++j);
} else {
break;
}
}
list.set(j, "");
charInNote += s.length();
} else if (note2 != -1) {// "//"类的单行注释
countNote++;
list.set(j, s.substring(0, note2));
charInNote += s.substring(note2).length() + 1;
} else if (note1 != -1 && note3 != -1) {//单行注释
countNote++;
String m1 = s.substring(0, note1);
String m2 = s.substring(note3 + 2);
String m3 = m1 + m2;
charInNote += s.substring(note1, note3 + 2).length();
list.set(j, m3);
} else {//删除输出语句
String rp = list.get(j);
rp = rp.replaceAll(dm, "");
list.set(j, rp);
}
}
return new int[]{countNote, charInNote};
}
}
第五个类:TheKeyWord
这里是自己敲出来的,没有用到正则表达式,纯想!
作用:寻找50个java的关键词
代码:
package finalExam;
import java.io.*;
import java.util.*;
public class TheKeyWord extends AnalyseDir {
public static final String[] KEYWORDS = {"abstract", "assert", "boolean", "break", "byte", "case",
"catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum",
"extends", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof",
"int", "interface", "long", "native", "new", "package", "private", "protected", "public",
"return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw",
"throws", "transient", "try", "void", "volatile", "while"};//50个关键字词
//有参构造
public TheKeyWord(String str) {
super(str);
}
public static void check(Map<String, Integer>map, String str) throws IOException {
try (
FileReader fr = new FileReader(str);
BufferedReader bfr = new BufferedReader(fr)
) {
String s, s1;
int is = 1;
while ((s1 = bfr.readLine()) != null) {//使用readLine方法,一次读一行
//对注释
if (s1.contains("/**") && is == 1) {
is = 0;
}else if(s1.contains("*/")){
is = 1;
}else if(is == 0 && s1.contains("*")){
}else if(is == 1) {
for (Map.Entry<String, Integer> entry : map.entrySet()) {
s = s1;
while (s.contains(entry.getKey())) {
//对复合词
if (((s.indexOf(entry.getKey()) != 0) && (s.charAt(s.indexOf(entry.getKey()) - 1) != ' ')) || ((s.indexOf(entry.getKey()) + entry.getKey().length() < s.length()) && ((s.charAt(s.indexOf(entry.getKey()) + entry.getKey().length()) != ' ')))) {
//对双引号
if (s.indexOf('"') >= 0 && (s.indexOf('"') < s.indexOf(entry.getKey())) && ((s.substring(s.indexOf('"') + 1)).indexOf('"') > 0) && ((s.substring(s.indexOf('"') + 1)).indexOf('"') > s.indexOf(entry.getKey()))) {
s = s.substring(s.indexOf('"') + 2 + ((s.substring(s.indexOf('"') + 1)).indexOf('"')));
if (s.contains(entry.getKey())) entry.setValue(entry.getValue() + 1);
}
} else {
entry.setValue(entry.getValue() + 1);
}
if (s.contains(entry.getKey()) && is == 1) {
s = s.substring(s.indexOf(entry.getKey()) + entry.getKey().length());
}
}
}
}
}
}
}
public static List makeMap(String str) throws IOException {
Map<String, Integer> map = new HashMap<>();
for (String keyword : KEYWORDS) {//初始化关键词map
map.put(keyword, 0);
}
if (str.contains(".java")) {
check(map, str);
} else {
File fdir0 = new File(str);
for (int i = 0; i < Objects.requireNonNull(fdir0.list()).length; i++) {//遍历每个文件
if (Objects.requireNonNull(fdir0.list())[i].contains(".java")) {
String strAddress = str + "/" + Objects.requireNonNull(fdir0.list())[i];
check(map, strAddress);
}
}
}
//自定义比较器
Comparator<Map.Entry<String, Integer>> valCmp = (o1, o2) -> {
// TODO Auto-generated method stub
return o2.getValue() - o1.getValue(); // 降序排序,如果想升序就反过来
};
//将map转成List,map的一组key,value对应list一个存储空间
List<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet()); //传入maps实体
list.sort(valCmp); //对list进行排序
return list;
}
}
第六个类(接口):CorrectRoad
作用:只是一个寻找/的函数,主要很多个地方都要用到,一个个都写的话,代码量多。
代码:
package finalExam;
public interface CorrectRoad {
static int nameWhere(String str) {//找出str中最后一个"/"的位置
int i;
for (i = str.length() - 1; i >= 0; i--) {
if (str.charAt(i) == '/') break;
}
return i;
}
}
类图:
总结:
每次这种课程设计不要太马虎去对待,!态度决定一切!。
一看到这种题,不要慌,现从主函数下手,构建好一个主体后,就先后实现每个小功能,然后在对自己完成的程序进行优化,这样的顺序下来做课程设计事半功倍。
然后不懂的那些,多想几天,多试几天,很容易开窍的,实在不会,实在试了很多天,还是不会,那就参考其它大佬的代码,读懂它们。
最后发现问题的请指出!