scau Java综合性实验之Java源程序分析程序

1. 编写一个Java应用程序,实现对某个目录中的所有Java源程序文件(包含该目录的子目录中的源程序文件)进行统计。统计内容包括:

(1) 目录中每个源程序文件的总行数和空白行数,文件的字节数;

(2) 目录中所有源程序文件合计总行数、合计空白行数、合计文件的字节数。

2. 具体实现要求如下:

(1) 程序运行首先显示如下所示的菜单:

(2) 选择菜单中第1项时,要求输入一个目录名

如果输入目录名称对应的目录不存在或不是目录,则输出:

[目录名称] 不是合法的目录名称!

例如:

如果是合法存在的目录,则对该目录中的Java源程序文件进行分析,分析内容包括:

细节部分:每个源程序文件的行数、其中空行数、字节数。

合计部分:源程序文件个数、源程序文件行数、其中空行数、总的字节数。

注意,分析时包括输入目录中的所有子目录。

分析的结果的保存要求:

在当前项目目录中建立一个名为result的目录,结果文件存放在该目录中。

结果文件是一个文本文件,命名方式是用被分析的目录名作为主文件名,扩展名为目录名。

例如:分析D:demo目录,结果文件名为“demo.txt”。

结果文件中数据存放格式如下示例:

其中,

第1行:被分析目录的完整名称

第2行:空行

第3行:Files detail:

第4行:被分析目录的短名称,前面有一个 + 号

第5行:从本行开始依次输出被分析目录中的子目录和源程序文件

如果是子目录,则该行是 + 号 和 子目录的短名称

如果是源程序文件,则该行以 - 号开始,依次是:文件名、总行数、空白行数、字节数

注意:一个目录中如果既有子目录也有源程序文件,则先依次排列子目录,再依次排列文件。并且要按照名称升序排序。同时,每深入一层子目录,要缩进4个空格。

第X行:Total:

第X+1行:目录中总文件个数

第X+2行:目录中总的行数

第X+3行:目录中总的空白行数

第X+4行:目录中总字节数

(3) 选择菜单中第2项时,

如果result目录中还没有结果文件,则显示:还没有分析结果!

如果result目录中已经有结果文件,则以下面格式显示文件列表:

可以查看的结果文件有:

输入文件编号后,显示输出结果文件的内容,如果输入0表示不查看任何结果。编号输入错误应该提示。

这两天写综合性实验写的颇有感触……

emm…代码中有详细的注释,本人也比较菜,没用啥高深的算法,应该一看就会…

要是有啥不懂的可以私信问我呐

Menu类

package Project;

import java.io.*;
import java.util.Scanner;

public class Menu {
    private final Scanner Sc = new Scanner(System.in);

    //菜单
    public void showMenu() {
        System.out.println("----------MENU----------");
        System.out.println("1.分析目录中的源程序文件");
        System.out.println("2.查看分析结果");
        System.out.println("0.退出程序");
        System.out.println("------------------------");
        System.out.print("请选择:");
        chooseMenu();
    }

    //选择菜单的选项
    public void chooseMenu() {
        int choice;
        choice = Sc.nextInt();
        switch (choice) {
            case 1 -> {
                analyseFiles();
                break;
            }
            case 2 -> {
                checkResult();
                break;
            }
            case 0 -> {
                System.out.println("您已成功退出程序!");
                System.exit(0);
            }
            default -> System.out.println("您输入的选项有误!");
        }
    }

    //分析目录中的源程序文件
    private void analyseFiles() {
        System.out.println();
        System.out.print("请输入目录名称:");
        String FileName = Sc.next();
        Directory.root = new File(FileName);
        Directory.PathName = String.valueOf(new File(FileName));
        if (!Directory.root.isDirectory()) {
            System.out.println("错误:[" + Directory.root + "]不是目录名或不存在!\n");
        } else {
            Directory.outPut();
            System.out.println("分析成功!");
        }

    }

    private void checkResult() {
        System.out.println();
        File file = new File("Result");
        File[] files = file.listFiles();
        if (files == null) {
            System.out.println("还没有分析结果!");
        } else {
            System.out.println("--------------------------------");
            for (int i = 0; i < files.length; i++) {
                System.out.println(i + 1 + "." + files[i].getName());
            }
            System.out.println("--------------------------------");
            System.out.println("请选择要查看的结果文件(0表示放弃):");
            int choice = Sc.nextInt();
            if (choice == 0) {
                return;
            }
            if (choice > files.length) {
                System.out.print("您输入的选项有误!请重新输入:");
                checkResult();
            }
            System.out.println("查询结果如下:");
            checkFile(files[choice - 1]);//选项是比文件类数组大1的
            System.out.println();
        }
    }

    private void checkFile(File file) {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(file));
            while (true) {
                String data = br.readLine();
                if (data == null) {
                    break;
                }
                System.out.println(data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            {
                if (br != null) {
                    try {
                        br.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

SourceFile类

package Project;

import java.io.*;

public class SourceFile {
    private File file;
    private long cntOfLines;//单个源码文件的行数
    private long cntOfBlank;//单个源码文件的空行数
    private long bytes;//单个源码文件的字节数

    public SourceFile(File file) {
        this.file = file;
    }

    //统计行数
    public long getCntOfLines() {
        //只有第一次调用才进行分析统计,节约时间
        if (cntOfLines == 0) {
            getCntOfLinesAndBlank();
        }
        return cntOfLines;
    }

    //统计空行数
    public long getCntOfBlank() {
        //同上
        if (cntOfBlank == 0) {
            getCntOfLinesAndBlank();
        }
        return cntOfBlank;
    }

    //统计字节数
    public long getBytes() {
        bytes = file.length();
        return bytes;
    }

    //统计行数和空行数
    public void getCntOfLinesAndBlank() {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(file));
            while (true) {
                String line = br.readLine();
                if (line == null) {
                    break;
                }
                cntOfLines++;//每读一行,行数加一
                if ("".equals(line)) {
                    cntOfBlank++;//读到空行,空行数加一
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {//关闭文件
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Directory类

package Project;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

public class Directory {
    static File root;//待分析的文件或文件夹
    static String PathName;//存放root路径
    /*static String dataPathName = "Result";//存储分析结果文件夹的路径
    static String testDataPath = "testData";//存放文件或文件夹的文件夹路径
    static File dataDir = new File(dataPathName);//存储分析结果文件夹的文件类
    static File testDir = new File(testDataPath);//存放测试文件或文件夹的文件夹的文件类*/
    static int level = 0;//用于记录递归的层数

    private static long AllOfLines;
    private static long AllOfBlank;
    private static long AllOfBytes;

    private static class GloBal {
        public static ArrayList<File> fileArrayList = new ArrayList<>();//存放待分析的.java文件
        public static ArrayList<String> outputData = new ArrayList<>();//存放分析结果的String
    }

    /*
        private static void saveFileList() {
            if (root.isDirectory()) {
                File[] files = root.listFiles();
                int len = files.length;
                for (int i = 0; i < len; i++) {
                    if (files[i].isDirectory()) {
                        root = files[i];
                        saveFileList();
                    } else {
                        if (files[i].getName().endsWith(".java")) {//如果以.java结尾
                            GloBal.fileArrayList.add(files[i]);//放入ArrayList中
                        }
                    }
                }
            }else if(root.isFile() && root.getName().endsWith(".java")){
                GloBal.fileArrayList.add(root);//放入ArrayList中
            }
        }
    */
    //开头格式
    private static void firstShow() {
        GloBal.outputData.add("[" + root.getAbsolutePath() + "]" + "  Result:");
        GloBal.outputData.add("");
        GloBal.outputData.add("Files detail:");
    }

    private static void saveFileList() {
        if (root.isDirectory()) {
            StringBuilder tapOfDirectory = getTap();
            GloBal.outputData.add(tapOfDirectory + "+" + root.getName());//目录

            File[] files = root.listFiles();//把root名下所有文件存进File[] files中
            int len = files.length;//root下文件或文件夹的个数
            //flag判断递归层数是否相同
            int flag = 0;
            for (int i = 0; i < len; i++) {//循环
                if (files[i].isDirectory()) {//如果是一个目录
                    root = files[i];//更新root路径
                    //递归层数相同时level只需要加一次
                    if (flag == 0) {
                        level++;
                        flag = 1;
                    }
                    saveFileList();//递归 以存储.java文件
                } else {//如果不是一个目录(即是一个文件)
                    if (files[i].getName().endsWith(".java")) {//如果以.java结尾
                        //感觉好像没必要放进ArrayList
                        //好吧好像有用,可以统计.java文件个数
                        GloBal.fileArrayList.add(files[i]);//放入ArrayList中

                        SourceFile SF = new SourceFile(files[i]);//使用SourceFile类进行统计

                        //处理文件名 方面后面对齐
                        StringBuilder newName = tidyFileName(files[i]);
                        //处理Total,方便Blank对齐
                        StringBuilder newTotal = tidyLines(files[i]);
                        //处理Blank,方便bytes对齐
                        StringBuilder newBlank = tidyBlank(files[i]);
                        //字符数
                        long bytes = SF.getBytes();

                        StringBuilder tapOfFiles = getTap();
                        GloBal.outputData.add(tapOfFiles
                                + "    -" + newName
                                + "\tTotal: " + newTotal
                                + "Blank: " + newBlank
                                + bytes + " bytes");

                        /*GloBal.outputData.add("    -" + files[i].getName()
                                +"\t\tTotal: " + SF.getCntOfLines()
                                + ",  Blank: " + SF.getCntOfBlank()
                                + ",  " + SF.getBytes() + " bytes");*/

                        //统计
                        AllOfLines += SF.getCntOfLines();
                        AllOfBlank += SF.getCntOfBlank();
                        AllOfBytes += SF.getBytes();
                    }
                }
            }
        } else if (root.isFile() && root.getName().endsWith(".java")) {//如果是文件且为.java文件则直接存储
            GloBal.fileArrayList.add(root);

            SourceFile SF = new SourceFile(root);
            StringBuilder newName = tidyFileName(root);
            StringBuilder newLines = tidyLines(root);
            StringBuilder newBlank = tidyBlank(root);

            GloBal.outputData.add("    -" + newName
                    + "\t Total:  " + newLines
                    + "Blank: " + newBlank
                    + SF.getBytes() + "  bytes");
            //统计
            AllOfLines += SF.getCntOfLines();
            AllOfBlank += SF.getCntOfBlank();
            AllOfBytes += SF.getBytes();
        }
    }

    //子目录比上一级缩进4格
    private static StringBuilder getTap() {
        StringBuilder tap = new StringBuilder();
        for (int i = 1; i <= level * 4; i++) {
            tap.append(" ");
        }
        return tap;
    }

    private static void saveTotal() {
        StringBuilder newCntOfFiles = tidyTotal(new StringBuilder(String.valueOf(getAllOfFiles())));
        StringBuilder newCntOfLines = tidyTotal(new StringBuilder(String.valueOf(getAllOfLines())));
        StringBuilder newCntOfBlank = tidyTotal(new StringBuilder(String.valueOf(getAllOfBlank())));
        StringBuilder newCntOfBytes = tidyTotal(new StringBuilder(String.valueOf(getAllOfBytes())));
        GloBal.outputData.add("Total:");
        GloBal.outputData.add("\t\t" + newCntOfFiles + "\tJava Files");
        GloBal.outputData.add("\t\t" + newCntOfLines + "\tlines in files");
        GloBal.outputData.add("\t\t" + newCntOfBlank + "\tblank lines");
        GloBal.outputData.add("\t\t" + newCntOfBytes + "\tbytes");
    }

    private static StringBuilder tidyTotal(StringBuilder str) {
        for (int i = str.length(); i < 8; i++) {
            str.append(" ");
        }
        return str;
    }

    private static StringBuilder tidyFileName(File file) {
        //处理文件名 方面后面对齐
        StringBuilder newName = new StringBuilder(file.getName());
        int size = file.getName().length();
        for (int j = size; j < 40; j++) {
            newName.append(" ");
        }
        return newName;
    }

    private static StringBuilder tidyLines(File file) {
        SourceFile SF = new SourceFile(file);//使用SourceFile类进行统计
        StringBuilder newLines = new StringBuilder(String.valueOf(SF.getCntOfLines()));
        int len = newLines.length();
        for (int j = len; j < 6; j++) {
            if (j == len) {
                newLines.append(",");
            }
            newLines.append(" ");
        }
        return newLines;
    }

    private static StringBuilder tidyBlank(File file) {
        SourceFile SF = new SourceFile(file);//使用SourceFile类进行统计
        StringBuilder newBlank = new StringBuilder(String.valueOf(SF.getCntOfBlank()));
        int lengthOfBlank = newBlank.length();
        for (int j = lengthOfBlank; j < 6; j++) {
            if (j == lengthOfBlank) {
                newBlank.append(",");
            }
            newBlank.append(" ");
        }
        return newBlank;
    }

    //对文件进行排序
    //这个可能有问题,后续要注意调试
    //如果一个目录里既有子目录又有文件,则先排子目录,再排文件
    private static void sortFiles(File[] files) {
        //使用匿名内部类构造比较器,对文件进行排序
        Arrays.sort(files, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                if (o1.isFile() && o2.isFile()) {//都是文件
                    return o1.getName().compareTo(o2.getName());
                } else if (o1.isDirectory() && o2.isDirectory()) {//都是目录
                    return o1.getName().compareTo(o2.getName());
                } else if (o1.isDirectory() && o2.isFile()) {//一个目录一个文件
                    return -1;
                } else if (o1.isFile() && o2.isDirectory()) {//一个文件一个目录
                    return 1;
                } else {
                    return 0;
                }
            }
        });
    }

    //统计.java文件个数
    public static int getAllOfFiles() {
        return GloBal.fileArrayList.size();
    }

    //统计所有.java文件中的字符个数
    /*private static void CountOfCharOfFile() {
        long SumOfChar = 0;
        for (int i = 0; i < GloBal.fileArrayList.size(); i++) {
            SumOfChar += GloBal.fileArrayList.get(i).length();
        }
    }*/

    //统计总行数
    public static long getAllOfLines() {
        return AllOfLines;
    }

    //统计空行数
    public static long getAllOfBlank() {
        return AllOfBlank;
    }

    //统计字符数
    public static long getAllOfBytes() {
        return AllOfBytes;
    }

    /*
     * 把分析结果保存到项目目录下的result文件夹
     * */
    public static void saveAnalyzeData() throws IOException {
        root = new File(PathName);//原输入的目录名称,在saveFileList经过递归后root不再是一开始的root了
        File resultFile = new File("Result");
        if (!resultFile.exists()) {
            resultFile.mkdir();
        }
        BufferedWriter AnalyzeDataIn = new BufferedWriter
                (new FileWriter(resultFile + "\\" + root.getName() + ".txt"));
        for (int i = 0; i < GloBal.outputData.size(); i++) {
            AnalyzeDataIn.write(GloBal.outputData.get(i));
            AnalyzeDataIn.newLine();
        }
        AnalyzeDataIn.flush();
    }

    /*
     * 运行各方法以分析文件
     * 保存分析数据
     * */
    public static void outPut() {
        firstShow();
        saveFileList();
        saveTotal();
        try {
            saveAnalyzeData();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Main类

package Project;

public class Main {
    public static void main(String[] args) {
        run();
    }
    public static void run(){
        Menu menu = new Menu();
        menu.showMenu();
        run();
    }
}

简要的需求分析: 已经提供的数据:   <1> 班级学生名单:学生的学号和姓名已经分别存储在以学生所在班级命名的文本文件中,每行存储一个学生的学号和姓名,中间用一个空格分隔。例如:2010级网络工程1班.txt文件中存放该班学生名单。内容为:   201030720102 陈纯   201030720103 陈娟娟   201030720104 陈伟健   201030720105 陈伊纯   ……   <2> 开设课程保存在名为:course.txt的文本文件中,每行存放一门课程的名称,如:   面向对象程序设计   操作系统   数据库系统      实现以下功能,所有功能以图形用户界面完成。   <1> 新建课程考试成绩单,功能描述如下: 程序界面显示已经开设的课程(从course.txt中读取),用户选择本次输入的课程。 用户选择为哪个班输入成绩(即选择相应班的名单文件)。如果该班的成绩已经输入(已经存在对应成绩单文件),则提示无需输入。 程序提供界面为该班的每个学生输入考试成绩。 输入的成绩单以对象文件格式存储到文件中,文件命名为:班级-课程名.dat。例如:2010级网络工程1班-面向对象程序设计.dat。   <2> 打开课程考试成绩单,功能描述如下: 用户选择打开的成绩单文件,程序打开并读取成绩单文件内容,并显示在界面中。   <3> 修改课程考试成绩单,功能描述如下: 打开某班某课程的成绩单后,可以选择修改其中某个或某几个考试成绩,并保存。   <4> 课程考试成绩分析,功能描述如下: 即打开某班某课程的成绩单后,点击成绩分析按钮或菜单,显示如下分析内容: 最高分:XX分,最低分:XX分,平均分:XX分 不及格(分数<60):XX人,占XX.XX% 及格(60<=分数<70):XX人,占XX.XX% 中等(70<=分数<80):XX人,占XX.XX% 良好(80<=分数<90):XX人,占XX.XX% 优秀(90<=分数<100):XX人,占XX.XX%   <5> 成绩图形分析,功能描述如下:    显示考试成绩分布的饼图和柱形图。 想用上饼状图和柱状图请看: http://www.open-open.com/lib/view/open1365997415828.html
实现以下功能,所有功能以图形用户界面完成。 <1> 新建课程考试成绩单,功能描述如下: n 程序界面显示已经开设的课程(从course.txt中读取),用户选择本次输入的课程。 n 程序界面通过文件对话框要求用户选择为哪个班输入成绩(即选择相应班的名单文件)。如果该班的成绩已经输入(已经存在对应成绩单文件),则提示无需输入。 n 程序提供界面为该班的每个学生输入考试成绩。 n 输入的成绩单以对象文件格式存储到文件中(不能是文本文件),文件命名为:班级-课程名.dat。例如:2010级网络工程1班-面向对象程序设计.dat。 <2> 打开课程考试成绩单,功能描述如下: n 程序通过文件对话框要求用户选择打开的成绩单文件。 n 程序打开并读取成绩单文件内容,并显示在界面中。 <3> 修改课程考试成绩单,功能描述如下: n 完成第<2>功能后,即打开某班某课程的成绩单后。 n 可以选择修改其中某个或某几个考试成绩,并保存。 <4> 课程考试成绩分析,功能描述如下: n 完成第<2>功能后,即打开某班某课程的成绩单后。 n 点击成绩分析按钮或菜单,显示如下分析内容: u 最高分:XX分,最低分:XX分,平均分:XX分 u 不及格(分数<60):XX人,占XX.XX% u 及格(60<=分数<70):XX人,占XX.XX% u 中等(70<=分数<80):XX人,占XX.XX% u 良好(80<=分数<90):XX人,占XX.XX% u 优秀(90<=分数<100):XX人,占XX.XX% <5> 成绩图形分析,功能描述如下:
SCAU编译综合性实验是指在南京农业大学计算机学院进行的综合性实践课程,旨在培养学生在编译原理方面的实际能力。该实验要求学生利用所学知识和技能,设计并实现一个基于某种编程语言的编译器。 在实验开始前,学生需要深入学习编译原理相关知识,包括词法分析、语法分析、语义分析、中间代码生成和目标代码生成等。同时,他们还需要掌握常用编译器工具,例如flex、bison等。 实验内容包括以下主要步骤: 1. 语法分析:根据给定的文法规则,设计并实现语法分析器。通过分析源代码的结构,并生成相应的语法树。 2. 语义分析:在语法分析的基础上,进一步检查和分析源代码的语义是否符合规范。例如,检查变量声明是否正确、类型是否匹配等。 3. 中间代码生成:将源代码翻译成中间代码,比如三地址码或四元式。中间代码是一种与具体机器无关的表示方式,可以方便地进行优化和后续的目标代码生成。 4. 目标代码生成:将中间代码翻译成目标机器上可执行的机器代码。这个过程需要考虑不同机器指令集的特点和限制,以及优化生成的目标代码。 5. 目标代码优化:对生成的目标代码进行优化,使其在运行效率和内存占用上更加优化。 6. 调试和测试:通过合适的测试用例检验编译器功能的正确性和性能。同时,对编译器进行调试,解决可能出现的错误和问题。 7. 实验报告:撰写实验报告,详细描述实现的过程、方法、结果和分析。 综合性实验的完成需要良好的编程能力和分析问题的能力。通过这个实验,学生不仅巩固了编译原理相关知识,还培养了解决实际问题的能力和团队合作精神。这对今后从事软件开发和计算机研究工作都具有重要意义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值