JAVA实现WC.exe功能

项目要求

实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
具体功能要求:
程序处理用户需求的模式为:

wc.exe [parameter] [file_name]

  • 基本功能列表:

    wc.exe -c file.c     //返回文件 file.c 的字符数(实现)

    wc.exe -w file.c    //返回文件 file.c 的词的数目 (实现)

    wc.exe -l file.c      //返回文件 file.c 的行数(实现)

  • 扩展功能:

  -s   递归处理目录下符合条件的文件。(实现)
  -a   返回更复杂的数据(代码行 / 空行 / 注释行)。(实现)

  • 高级功能:

  -x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。

  (实现)

  需求举例:
    wc.exe -s -a *.c (实现)

    返回当前目录及子目录中所有*.c 文件的代码行数、空行数、注释行数。


 Github项目地址:https://github.com/kvhong/JAVA-to-bulid-wc.exe

 解题思路

主函数思路:通过输入的命令来判断执行各个功能函数,将输入的命令分割为两部分,第一部分是指令,第二部分是文件路径,用指令来调用各个功能函数,文件路径则作为参数输入到各个功能函数中实现功能。

功能函数的共同部分:通过传入的文件路径并用Buffer流读取文件内容,对文件内容进行相应统计然后输出结果,需实现文件不存在或者路径输入错误提示。

词数统计函数:需要去除空行、各种符号,将独立的词统计出来;

字符统计函数:需要去除空行、空格,将其余的内容的每一个单元都示为一个字符统计出来;

行数统计函数:文件中全部行数除结尾空行不算外其他都统计;

空行:TAB、空格、回车形成的空行都算入;

注释行:单独存在//、/*、*/的算入注释行;

代码行:除了空行、注释行外属于代码行;

帮助:将各种命令列出,以更好的帮助使用;

 用户使用说明

-c 文件(须包含文件完整路径) 统计程序文件中的字符数
-w 文件(须包含文件完整路径) 统计程序文件中的单词数
-l 文件(须包含文件完整路径) 统计程序文件中的行数
-a 文件(须包含文件完整路径) 统计程序文件中的空行数、代码行数、注释行数
-s-a 文件路径或者文件夹路径 递归统计程序文件中的空行数、代码行数、注释行数(-s 命令不可单独使用)
? 帮助
end 结束程序

 

 遇到的问题及解决方法

一开始不知道如何对词、字符进行有效的分割,然后在网上学习到可以用正则表达式进行分割:使用下面语句将数字和和中英文标点符号和中文都替换成空格,以通过空格分割出各个词。

str.replaceAll("[\\p{Nd}\\u9fa5-\\uffe5\\p{Punct}\\s&&[^-]]", " ");

使用下面的正则匹配器匹配注释行和空行

Pattern nodeLinePattern = Pattern.compile("((//)|(/\\*+)|((^\\s)*\\*)|((^\\s)*\\*+/))+", 
                        Pattern.MULTILINE + Pattern.DOTALL);    // 注释匹配器(匹配单行、多行、文档注释)
Pattern spaceLinePattern = Pattern.compile("^\\s*$");    // 空白行匹配器(匹配回车、tab键、空格)

 设计及代码

 该程序通过一个主函数文件和一个方法函数文件组成。主函数文件用于输入参数并判断所输入的各种操作以调用方法函数。方法函数用于实现各种功能:

文件词数统计函数:getwordnumber()  命令:-w  文件路径(须包含完整路径)

 

//文件词统计函数
    int getwordnumber(String filename) throws IOException {int num=0;
        String[] strword = null;
        File file = new File(filename);
        if(file.exists()) {
            //读取文件
            FileReader fr = new FileReader(filename);
            br = new BufferedReader(fr);
            String line = null;
            StringBuffer sbf = new StringBuffer();
            while((line=br.readLine())!= null) {
                sbf.append(line);
                String str = sbf.toString();
                //正则表达式替换符号
                str = str.replaceAll("[\\p{Nd}\\u9fa5-\\uffe5\\p{Punct}\\s&&[^-]]", " ");
                //按空格将内容分割
                strword = str.split("\\s+");
                num=strword.length;
            }
            br.close();
            fr.close();
        }else {
            System.out.println("文件不存在,请重新输入文件!");
        }
        return num;
    }

 

文件字符数统计函数:getCharacternumber()  命令:-c  文件路径(须包含完整路径)

 

//文件字符统计函数
    int getCharacternumber(String filename) throws IOException {int number = 0;
        String[] strword = null;
        File file = new File(filename);
        if(file.exists()) {
            //读取文件
            FileReader fr = new FileReader(filename);
            br = new BufferedReader(fr);
            String line = null;
            String str=null;
            StringBuffer sbf = new StringBuffer();
            while((line=br.readLine())!= null) {
                sbf.append(line);
                str = sbf.toString();
                strword = str.split("\\s+");
            }
            for(int i=0;i) {
                Pattern pattern = Pattern.compile("[0-9a-zA-Z]*");
                Matcher matcher = pattern.matcher(strword[i]);
                if(matcher.find()) {
                    number+=matcher.regionEnd();
                }
            }
            br.close();
            fr.close();
        }else {
            System.out.println("文件不存在,请重新输入文件!");
        }
        return number;
    }

 

文件行数统计函数:getlinenumber()  命令:-l  文件路径(须包含完整路径)

 

//文件行数统计函数
    int getlinenumber(String filename) throws IOException {int linenum = 0;
        File file = new File(filename);
        if(file.exists()) {
            //读取文件
            FileReader fr = new FileReader(filename);
            //读取文件行数
            LineNumberReader lnr = new LineNumberReader(fr);
            while(lnr.readLine()!= null) {
                linenum=lnr.getLineNumber();
            }
            lnr.close();
            fr.close();
        }else {
            System.out.println("文件不存在,请重新输入文件!");
        }
        return linenum;
    }

 

统计文件或者文件夹中程序文件的空行、代码行、注释行,有递归文件目录功能的函数,将递归功能一同实现在此函数中:diffline()  命令:-a 文件路径或文件夹路径/-s-a 文件路径或文件夹路径

 

//统计文件或者文件夹中程序文件的空行、代码行、注释行,有递归文件目录功能
        int[] difflineGUI(File file) throws IOException {
            int spaceline = 0;
            int nodeline = 0;
            int codeline = 0;
            if (file == null || !file.exists()) {
                System.out.println(file + ",文件不存在!");
            }else {
            if (file.isDirectory()) {
                File[] files = file.listFiles(new FileFilter() {
                    public boolean accept(File pathname) {
                        return pathname.getName().endsWith(".java")|| pathname.isDirectory()||
                                pathname.getName().endsWith(".c")||pathname.getName().endsWith(".cpp")  ;
                    }
                });
                //递归文件目录
                for (File target : files) {
                    diffline(target);
                }
            } else {
                BufferedReader bufr = null;
                    // 将指定路径的文件与字符流绑定
                    bufr = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
     
                // 定义匹配每一行的正则匹配器
                Pattern nodeLinePattern = Pattern.compile("((//)|(/\\*+)|((^\\s)*\\*)|((^\\s)*\\*+/))+", 
                        Pattern.MULTILINE + Pattern.DOTALL);    // 注释匹配器(匹配单行、多行、文档注释)
     
                Pattern spaceLinePattern = Pattern.compile("^\\s*$");    // 空白行匹配器(匹配回车、tab键、空格)
                
                // 遍历文件中的每一行,并根据正则匹配的结果记录每一行匹配的结果
                String line = null;
                    while((line = bufr.readLine()) != null) {
                        if (nodeLinePattern.matcher(line).find()) {
                            nodeline ++;
                        }else if (spaceLinePattern.matcher(line).find()) {
                            spaceline ++;
                        }else{
                            codeline ++;
                        } 
                    }
            }
            }
            int[] Sline= {spaceline,nodeline,codeline};
            return Sline;
        }

用于wctest.java文件:

//统计文件或者文件夹中程序文件的空行、代码行、注释行,有递归文件目录功能
        void diffline(File file) throws IOException {
            int spaceline = 0;
            int nodeline = 0;
            int codeline = 0;
            if (file == null || !file.exists()) {
                System.out.println(file + ",文件不存在!");
            }else {
            if (file.isDirectory()) {
                File[] files = file.listFiles(new FileFilter() {
                    public boolean accept(File pathname) {
                        return pathname.getName().endsWith(".java")|| pathname.isDirectory()||
                                pathname.getName().endsWith(".c")||pathname.getName().endsWith(".cpp")  ;
                    }
                });
                //递归文件目录
                for (File target : files) {
                    diffline(target);
                }
            } else {
                System.out.println("文件名:"+file.getAbsolutePath());
                // 将指定路径的文件与字符流绑定
                BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
                // 定义匹配每一行的正则匹配器
                Pattern nodeLinePattern = Pattern.compile("((//)|(/\\*+)|((^\\s)*\\*)|((^\\s)*\\*+/))+", 
                        Pattern.MULTILINE + Pattern.DOTALL);    // 注释匹配器(匹配单行、多行、文档注释)
     
                Pattern spaceLinePattern = Pattern.compile("^\\s*$");    // 空白行匹配器(匹配回车、tab键、空格)
                
                // 遍历文件中的每一行,并根据正则匹配的结果记录每一行匹配的结果
                String line = null;
                    while((line = bufr.readLine()) != null) {
                        if (nodeLinePattern.matcher(line).find()) {
                            nodeline ++;
                        }else if (spaceLinePattern.matcher(line).find()) {
                            spaceline ++;
                        }else{
                            codeline ++;
                        } 
                    }
                System.out.println("空行数:"+spaceline);
                System.out.println("注释行数:"+nodeline);
                System.out.println("代码行数:"+codeline);
            }
            }
        }
用于wctest.java的统计空行、注释行、代码行函数

帮助函数:help()  命令:?

 

//帮助函数
        void help(){
            System.out.println("-c 文件(须包含文件完整路径)   统计程序文件中的字符数");
            System.out.println("-w 文件(须包含文件完整路径)   统计程序文件中的单词数");
            System.out.println("-l 文件(须包含文件完整路径)   统计程序文件中的行数");
            System.out.println("-a 文件(须包含文件完整路径)   统计程序文件中的空行数、代码行数、注释行数");
            System.out.println("-s-a 文件路径或者文件夹路径    递归统计程序文件中的空行数、代码行数、注释行数");
            System.out.println("-x 打开用户界面");
            System.out.println("? 帮助");
            System.out.println("end 结束程序");
        }

主函数:wctest.java

 

package wc;

import java.io.File;
import java.io.IOException;
import java.util.Scanner;

public class wctest{
    
    private static Scanner scanner;

    public static void main(String[] args) throws IOException{
        String str = null;
        wcfunction wcf = new wcfunction();
        //循环询问命令输入
        while(true) {
            System.out.print("请输入命令:");
            //命令输入
            scanner = new Scanner(System.in);
            if(scanner.hasNext()) {
            str=scanner.nextLine();
            }
            //分割命令,第一个作为判断第二个为文件路径
            String[] strword = str.split(" ");
            if(strword.length==2) {
            if(strword[0].equals("-c")) {
                int chara=wcf.getCharacternumber(strword[1]);
                System.out.println("该文件的字符数:"+chara);
            }else if(strword[0].equals("-w")) {
                int word=wcf.getwordnumber(strword[1]);
                System.out.println("该文件的词数:"+word);
            }else if(strword[0].equals("-l")) {
                int line=wcf.getlinenumber(strword[1]);
                System.out.println("该文件的行数:"+line);
            }else if(strword[0].equals("-a")) {
                File file = new File(strword[1]);
                int[] linenum=wcf.diffline(file);
                System.out.println("该文件的空行数:"+linenum[0]);
                System.out.println("该文件的注释行数:"+linenum[1]);
                System.out.println("该文件的代码行数:"+linenum[2]);
            }else if(strword[0].equals("-s-a")) {
                File file = new File(strword[1]);
                int[] linenum=wcf.diffline(file);
                System.out.println("该文件的空行数:"+linenum[0]);
                System.out.println("该文件的注释行数:"+linenum[1]);
                System.out.println("该文件的代码行数:"+linenum[2]);
            }
            }else {
                if(strword[0].equals("?")) {
                    wcf.help();
                }else if(strword[0].equals("-x")) {
                    wcGUI.main(null);
                }else if(strword[0].equals("end")) {
                    break;
                }else {
                    System.out.println("命令输入错误,请重新输入!");
                }
            }
                
        }
    }
}

用户界面文件:wcGUI.java  命令:-x

package wc;

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.JScrollPane;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.SwingConstants;

public class wcGUI {

    private JFrame frmWc;
    private JTextField codenum;
    private JTextField wordnum;
    private JTextField linenum;
    private JTextField spaceline;
    private JTextField nodeline;
    private JTextField codeline;
    File file;
    wcfunction wcf=new wcfunction();

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    wcGUI window = new wcGUI();
                    window.frmWc.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public wcGUI() {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frmWc = new JFrame();
        frmWc.setTitle("wc");
        frmWc.setResizable(false);
        frmWc.setBounds(280, 50, 800, 600);
        frmWc.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        JPanel panel = new JPanel();
        frmWc.getContentPane().add(panel, BorderLayout.CENTER);
        panel.setLayout(null);
        
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setBounds(291, 0, 493, 562);
        panel.add(scrollPane);
        
        JPanel panel_1 = new JPanel();
        scrollPane.setViewportView(panel_1);
        panel_1.setLayout(new BorderLayout(0, 0));
        
        JTextArea textArea = new JTextArea();
        textArea.setEditable(false);
        panel_1.add(textArea, BorderLayout.CENTER);
        
        JButton choosefile = new JButton("选择文件");
        choosefile.setBounds(10, 32, 122, 23);
        panel.add(choosefile);
        
        JLabel filename = new JLabel("文件名:");
        filename.setBounds(10,70,80,23);
        panel.add(filename);
        
        JTextArea filepath = new JTextArea();
        filepath.setBounds(10, 95, 260, 40);
        filepath.setLineWrap(true);
        filepath.setWrapStyleWord(true); 
        filepath.setEditable(false);
        panel.add(filepath);
        
        JButton code = new JButton("统计字符数");
        code.setBounds(10, 277, 100, 30);
        panel.add(code);
        
        JButton word = new JButton("统计词数");
        word.setBounds(10, 317, 93, 30);
        panel.add(word);
        
        JButton line = new JButton("统计行数");
        line.setBounds(10, 357, 93, 30);
        panel.add(line);
        
        JButton diffline = new JButton("统计空行、注释行、代码行");
        diffline.setBounds(10, 397, 224, 30);
        panel.add(diffline);
        
        codenum = new JTextField();
        codenum.setBounds(120, 278, 93, 29);
        codenum.setEditable(false);
        panel.add(codenum);
        codenum.setColumns(10);
        
        wordnum = new JTextField();
        wordnum.setBounds(113, 318, 93, 30);
        wordnum.setEditable(false);
        panel.add(wordnum);
        wordnum.setColumns(10);
        
        linenum = new JTextField();
        linenum.setColumns(10);
        linenum.setBounds(113, 358, 93, 30);
        linenum.setEditable(false);
        panel.add(linenum);
        
        spaceline = new JTextField();
        spaceline.setColumns(10);
        spaceline.setBounds(70, 437, 93, 29);
        spaceline.setEditable(false);
        panel.add(spaceline);
        
        nodeline = new JTextField();
        nodeline.setColumns(10);
        nodeline.setBounds(70, 476, 93, 29);
        nodeline.setEditable(false);
        panel.add(nodeline);
        
        codeline = new JTextField();
        codeline.setColumns(10);
        codeline.setBounds(70, 515, 93, 30);
        codeline.setEditable(false);
        panel.add(codeline);
        
        JLabel label = new JLabel("空行");
        label.setHorizontalAlignment(SwingConstants.CENTER);
        label.setBounds(10, 437, 54, 29);
        panel.add(label);
        
        JLabel label_1 = new JLabel("注释行");
        label_1.setHorizontalAlignment(SwingConstants.CENTER);
        label_1.setBounds(10, 476, 54, 29);
        panel.add(label_1);
        
        JLabel label_2 = new JLabel("代码行");
        label_2.setHorizontalAlignment(SwingConstants.CENTER);
        label_2.setBounds(10, 515, 54, 30);
        panel.add(label_2);
        
        JLabel label_3 = new JLabel("个");
        label_3.setBounds(216, 277, 54, 30);
        panel.add(label_3);
        
        JLabel label_4 = new JLabel("个");
        label_4.setBounds(216, 317, 54, 30);
        panel.add(label_4);
        
        JLabel label_5 = new JLabel("行");
        label_5.setBounds(216, 357, 54, 30);
        panel.add(label_5);
        
        JLabel label_6 = new JLabel("行");
        label_6.setBounds(173, 437, 54, 30);
        panel.add(label_6);
        
        JLabel label_7 = new JLabel("行");
        label_7.setBounds(173, 476, 54, 30);
        panel.add(label_7);
        
        JLabel label_8 = new JLabel("行");
        label_8.setBounds(173, 515, 54, 30);
        panel.add(label_8);
        
        choosefile.addActionListener(new ActionListener() {
            
            @Override
            public void actionPerformed(ActionEvent e) {
                JFileChooser filechooser = new JFileChooser(".");
                int result=filechooser.showOpenDialog(null);
                if(result == JFileChooser.APPROVE_OPTION){
                file=filechooser.getSelectedFile();
                if(file!=null) {
                    if(textArea.getText()!=null) {
                        textArea.setText("");
                    }
                    if(filepath.getText()!=null) {
                        filepath.setText("");
                        filepath.setText(file.getPath());
                    }
                    try {
                        InputStreamReader read = new InputStreamReader(new FileInputStream(file), "GB2312");
                        BufferedReader br = new BufferedReader(read);
                        String line=null;
                        while((line=br.readLine())!=null) {
                            textArea.append(line+"\r\n");
                        }
                        br.close();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                }
            }
            }
        });
        
        code.addActionListener(new ActionListener() {
            
            @Override
            public void actionPerformed(ActionEvent e) {
                if(file.exists()) {
                    String filename = file.getAbsolutePath();
                    try {
                        int chara=wcf.getCharacternumber(filename);
                        codenum.setText(chara+"");
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    
                }
                
            }
        });
        
        word.addActionListener(new ActionListener() {
                    
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if(file.exists()) {
                            String filename = file.getAbsolutePath();
                            try {
                                int word=wcf.getwordnumber(filename);
                                wordnum.setText(word+"");
                            } catch (IOException e1) {
                                // TODO Auto-generated catch block
                                e1.printStackTrace();
                            }
                            
                        }
                        
                    }
                });

        line.addActionListener(new ActionListener() {
            
            @Override
            public void actionPerformed(ActionEvent e) {
                if(file.exists()) {
                    String filename = file.getAbsolutePath();
                    try {
                        int line=wcf.getlinenumber(filename);
                        linenum.setText(line+"");
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    
                }
                
            }
        });

        diffline.addActionListener(new ActionListener() {
            
            @Override
            public void actionPerformed(ActionEvent e) {
                if(file.exists()) {
                    try {
                        int[] line=wcf.difflineGUI(file);
                        spaceline.setText(line[0]+"");
                        nodeline.setText(line[1]+"");
                        codeline.setText(line[2]+"");
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    
                }
                
            }
        });
    }
}
用户界面wcGUI.java

测试

已将程序打包成wc.exe文件,放在Github项目中。可直接打开exe文件输入命令和文件路径直接使用,也可使用CMD命令行运行wc.exe再输入命令和文件路径使用。

CMD命令行使用:

JAVA实现WC.exe功能_第1张图片

exe直接使用:

JAVA实现WC.exe功能_第2张图片

用户界面:

JAVA实现WC.exe功能_第3张图片

 

JAVA实现WC.exe功能_第4张图片

代码覆盖率

JAVA实现WC.exe功能_第5张图片

代码覆盖率:97.5%

PSP

 

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 45 60
· Estimate · 估计这个任务需要多少时间 45 60
Development 开发 1290 1540
· Analysis · 需求分析 (包括学习新技术) 80 120
· Design Spec · 生成设计文档 40 60
· Design Review · 设计复审 (和同事审核设计文档) 40 40
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 30
· Design · 具体设计 120 160
· Coding · 具体编码 780 920
· Code Review · 代码复审 80 90
· Test · 测试(自我测试,修改代码,提交修改) 120 120
Reporting 报告 240 380
· Test Report · 测试报告 120 220
· Size Measurement · 计算工作量 30 40
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 90 120
  合计 1575 1980

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

总结

先是从课堂上更加意识到制定设计流程的重要性,了解到计划的设计不仅仅只存在于流程,对时间的规划也是非常重要的。在编写代码过程中实质遇到的困难是在如何实现词数的统计、正则表达式以及对文件目录的递归实现。最后通过学习正则表达式实现了对词数的统计,也进一步学习了正则表达式这一较为陌生的概念。也学会了一些新的插件和软件的使用:Eclipse的EclEmma插件用于代码覆盖率的统计;Git Bash软件对本地项目上传到Github使用;exe4j软件对JAVA项目进行EXE文件的打包使用。这次的项目作业对我来说是受益匪浅,对以后的项目实行有很大的帮助。

转载于:https://www.cnblogs.com/K-mengmengpi/p/9614405.html

你可能感兴趣的:(java,git,开发工具)