个人项目(wc.exe)
一、项目在GitHub上的地址:
·https://github.com/DawsonHuang/Word_Count
二、项目描述:
·项目名:WordCount(以下简称WC或项目)
·项目简述:实现一个统计程序wc.exe,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他
扩展功能,并能够快速地处理多个文件
·项目具体需求:
·程序处理用户需求的模式为:
·wc.exe [parameter] [file_name] //[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 返回更复杂的数据(代码行 / 空行 / 注释行)。
·注:
·①空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,
例如“{”。
·②代码行:本行包括多于一个字符的代码。
·③注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面
加注释:} //注释,在这种情况下,这一行属于注释行。
·需求举例:
·wc.exe -s -a *.c //返回当前目录及子目录中所有*.c 文件的代码行、空行、注释行数。
·高级功能: 【已实现】
· -x 参数:这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选
取单个文件,程序就会显示文件的字符数、行数等全部统计信息。
三、设计思路:
·根据输入的需要处理的文件地址,通过IO流来进行文件读取,然后再根据键入的命令类型,调用不同的方法
处理,运用readLine()方法对文件进行按行读取,通过split()方法计算出字符数、单词数、行数;再通过正
则表达式,matches()方法识别统计空行、注释行、代码行;将基本功能封装在BasicOperation方法中,统计
特殊行的功能封装在SpecialLine方法中,递归查找文件的方法为RecrusiveOperation
·编程语言:Java
·功能代码块:
·main()方法:接收输入、调用功能块
·基本功能:BasicOperation(String order,String[] input){....}
·查找特殊行:SpecialLine(String Path){....}
·递归查找文件:RecrusiveOperation(String endwith_name,String[] input,String path){....}
·流程图:
四、代码分析:
·主函数:
1 /** 2 * 本程序支持的命令行格式: 3 * 1、wc.exe -c 文件路径 4 * 2、wc.exe -w 文件路径 5 * 3、wc.exe -l 文件路径 6 * 4、wc.exe -a 文件路径 7 * 5、wc.exe -s -a 文件路径 8 * 6、wc.exe -x 文件路径 9 */ 10 public static void main(String[] args) throws Exception{ 11 Scanner in = new Scanner(System.in); 12 System.out.println("请注意输入格式 'wc.exe [order] [file_name]:"); 13 String[] input = in.nextLine().split(" "); 14 System.out.println(Arrays.toString(input)); 15 if("-s".equals(input[1])) { //输入类型是 wc.exe -s .... 16 System.out.println("请输入你要在以上目录中递归查找的文件的后缀:"); 17 String endwith_name = in.nextLine(); //后缀名 18 String path; //输入的要递归查询的目录 19 path = input[3]; //文件夹路径 20 RecrusiveOperation(endwith_name,input,path); 21 }else if("-a".equals(input[1])){ //输入类型是 wc.exe -a .... 22 SpecialLine(input[2]); 23 }else{ 24 String order = input[1]; 25 BasicOperation(order,input); 26 } 27 }
·基本需求:
1 //基本功能实现(-c / -w / -l) 2 public static void BasicOperation(String order,String[] input) throws Exception{ 3 int Count_Char=0; 4 int Count_Word=0; 5 int Count_Line=0; 6 FileInputStream file = new FileInputStream(input[2]); 7 InputStreamReader is = new InputStreamReader(file); 8 BufferedReader br = new BufferedReader(is); 9 String s; 10 switch(order) { 11 //计算字符数 12 case "-c": 13 while((s=br.readLine())!=null){ 14 Count_Char += s.length(); 15 } 16 System.out.println("字符数:"+Count_Char); 17 break; 18 //计算单词数 19 case "-w": 20 while((s=br.readLine())!=null){ 21 Count_Word += s.split(" ").length; 22 } 23 System.out.println("单词数:"+Count_Word); 24 break; 25 //计算行数 26 case "-l": 27 while(br.readLine()!=null){ 28 Count_Line++; 29 } 30 System.out.println("行数:"+Count_Line); 31 break; 32 default: 33 System.out.println("输入错误!"); 34 } 35 }
·递归查找文件
1 //扩展功能实现:-s 递归处理目录下符合条件的文件 2 public static void RecrusiveOperation(String endwith_name,String[] input,String path) throws Exception{ 3 List<String> fileNameList = new ArrayList<>();//文件名列表 4 List<String> filePathList = new ArrayList<>(); 5 File f = new File(path); 6 if (!f.exists()) { //路径不存在 7 System.out.println("该路径不存在!"); 8 }else{ 9 if(f.isFile()){ //路径为文件 10 if(f.getName().indexOf(endwith_name)!=-1) { 11 fileNameList.add(f.getName()); 12 filePathList.add(f.getAbsolutePath()); 13 } 14 }else if(f.isDirectory()){ //路径为文件夹 15 File fa[] = f.listFiles(); //获取改目录下所有文件和目录的绝对路径 16 System.out.println(Arrays.toString(fa)); 17 for (int i = 0; i < fa.length; i++) { 18 File fs = fa[i]; 19 if(fs.isFile()){ 20 if(fs.getName().indexOf(endwith_name)!=-1){ 21 fileNameList.add(fs.getName()); 22 filePathList.add(fs.getAbsolutePath()); 23 } 24 }else if(fs.isDirectory()) { 25 RecrusiveOperation(endwith_name, input, fs.getAbsolutePath()); 26 } 27 } 28 } 29 } 30 for(int i = 0;i < filePathList.size();i++){ 31 //System.out.println(fileNameList.get(i)); 32 System.out.println(filePathList.get(i)+":"); 33 switch(input[2]) { 34 case "-c": 35 BasicOperation("-c", input); break; 36 case "-w": 37 BasicOperation("-w", input); break; 38 case "-l": 39 BasicOperation("-l", input); break; 40 case "-a": 41 SpecialLine(filePathList.get(i)); break; 42 } 43 System.out.println(); 44 } 45 }
·查找特殊行
1 //扩展功能实现:-a 返回更复杂的数据(代码行/空行/注释行) 2 public static void SpecialLine(String path) throws Exception { 3 int BlankLine = 0; //空行数 4 int AnnotationLine = 0; //注释行数 5 int CodeLine = 0; //代码行数 6 FileInputStream file = new FileInputStream(path); 7 InputStreamReader is = new InputStreamReader(file); 8 BufferedReader br = new BufferedReader(is); 9 String s; 10 //正则表达式表示空行 11 String RegexBlankLine = "(\\{|\\})?\\s*"; //空行 12 //正则表达式表示单行注释 13 String RegexAnnotation = "\\s*(\\{|\\})?\\s*//.*"; //单行注释符‘//’ 14 //正则表达式表示多行注释 15 String RegexAnnotationStart = "\\s*(\\{||\\})?/\\*.*"; //多行注释起始符‘/*’ 16 String RegexAnnotationEnd = ".*\\*/"; //多行注释结束符‘*/’ 17 while ((s = br.readLine()) != null) { 18 if (s.matches(RegexBlankLine)) { 19 BlankLine++; 20 } else if (s.matches(RegexAnnotation)) { 21 AnnotationLine++; 22 } else if (s.matches(RegexAnnotationStart)) { 23 do { 24 AnnotationLine++; 25 s = br.readLine(); 26 } while (!s.matches(RegexAnnotationEnd)); 27 AnnotationLine++; //多行注释结束符所在行也应加上 28 } else { 29 CodeLine++; 30 } 31 } 32 System.out.println("空白行:" + BlankLine + " 行."); 33 System.out.println("注释行:" + AnnotationLine + " 行."); 34 System.out.println("代码行:" + CodeLine + " 行."); 35 }
public static void _x(){
new FileFrame();
}
·-x
1 package JFrame; 2 3 import com.HDH.WC; 4 5 import java.awt.*; 6 import java.awt.event.ActionEvent; 7 import java.awt.event.ActionListener; 8 import java.io.File; 9 import javax.swing.*; 10 11 /** 12 * GUI 13 * @author Dawson_Huang 14 * @Date 2020/3/14 - 9:27 15 */ 16 17 public class FileFrame implements ActionListener { 18 JFrame frame = new JFrame("文件选择");// 框架布局 19 JTabbedPane tabPane = new JTabbedPane();// 选项卡布局 20 Container con = new Container(); 21 JLabel label1 = new JLabel("文件目录"); 22 JTextField text1 = new JTextField();// TextField 目录的路径 23 JButton button1 = new JButton("选择");// 选择 24 JFileChooser jfc = new JFileChooser();// 文件选择器 25 JLabel label2 = new JLabel("选择命令"); 26 JCheckBox jCheckBoxC = new JCheckBox("-c"); 27 JCheckBox jCheckBoxW = new JCheckBox("-w"); 28 JCheckBox jCheckBoxL = new JCheckBox("-l"); 29 JCheckBox jCheckBoxA = new JCheckBox("-a"); 30 JButton button2 = new JButton("确定");// 31 32 public static void main(String[] args) { 33 new FileFrame(); 34 } 35 36 public FileFrame() { 37 jfc.setCurrentDirectory(new File("d://")); // 文件选择器的初始目录为d盘 38 double lx = Toolkit.getDefaultToolkit().getScreenSize().getWidth(); // 显示屏幕的宽度 39 double ly = Toolkit.getDefaultToolkit().getScreenSize().getHeight(); // 显示屏幕的高度 40 frame.setLocation(new Point((int) (lx / 2) - 150, (int) (ly / 2) - 150)); // 窗口出现的位置 41 frame.setSize(500, 360);// 设定窗口大小 42 frame.setContentPane(tabPane);// 设置布局 43 label1.setBounds(10, 10, 70, 20); 44 text1.setBounds(75, 10, 120, 20); 45 button1.setBounds(210, 10, 50, 20); 46 label2.setBounds(10, 60, 70, 20); 47 button2.setBounds(240, 60, 60, 20); 48 jCheckBoxC.setBounds(80, 60, 40, 20); 49 jCheckBoxW.setBounds(120, 60, 40, 20); 50 jCheckBoxL.setBounds(140, 60, 40, 20); 51 jCheckBoxA.setBounds(180, 60, 40, 20); 52 button1.addActionListener(this); // 添加事件处理 53 button2.addActionListener(this); // 添加事件处理 54 con.add(label1); 55 con.add(text1); 56 con.add(button1); 57 con.add(label2); 58 con.add(jCheckBoxC); 59 con.add(jCheckBoxW); 60 con.add(jCheckBoxL); 61 con.add(jCheckBoxA); 62 con.add(button2); 63 frame.setVisible(true);// 窗口可见 64 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 使能关闭窗口,结束程序 65 tabPane.add("1面板", con);// 添加布局1 66 } 67 68 public String path; 69 public String order; 70 /** 71 * 时间监听的方法 72 */ 73 public void actionPerformed(ActionEvent e) { 74 if (e.getSource().equals(button1)) { // 判断触发方法的按钮是哪个 75 int state = jfc.showOpenDialog(null);// 此句是打开文件选择器界面的触发语句 76 if (state == 1) { 77 return; 78 } else { 79 File f = jfc.getSelectedFile();// f为选择到的目录 80 text1.setText(f.getAbsolutePath()); 81 path = f.getAbsolutePath(); 82 } 83 } 84 if (e.getSource().equals(button2)) { 85 JCheckBox[] jcbs = {jCheckBoxA, jCheckBoxC, jCheckBoxL, jCheckBoxW}; 86 for (JCheckBox j : jcbs) { 87 if (j.isSelected()) { 88 order = j.getText(); 89 switch(order) { 90 case "-c": 91 try { 92 WC.BasicOperation("-c",path); 93 } catch (Exception ex) { 94 ex.printStackTrace(); 95 } 96 break; 97 case "-w": 98 try { 99 WC.BasicOperation("-c",path); 100 } catch (Exception ex) { 101 ex.printStackTrace(); 102 } 103 break; 104 case "-l": 105 try { 106 WC.BasicOperation("-c",path); 107 } catch (Exception ex) { 108 ex.printStackTrace(); 109 } 110 break; 111 case "-a": 112 try { 113 WC.SpecialLine(path); 114 } catch (Exception ex) { 115 ex.printStackTrace(); 116 } 117 break; 118 } 119 } 120 } 121 } 122 } 123 }
五、测试运行:
·文件目录:
·测试结果:
·命令"-c":
·命令"-w":
·命令"-l":
·命令"-s -a":
·命令"-x":
六、PSP2.1表格:
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
25 |
20 |
· Estimate |
· 估计这个任务需要多少时间 |
60 |
60 |
Development |
开发 |
180 |
240 |
· Analysis |
· 需求分析 (包括学习新技术) |
20 |
20 |
· Design Spec |
· 生成设计文档 |
30 |
30 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
20 |
20 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
10 |
· Design |
· 具体设计 |
60 |
60 |
· Coding |
· 具体编码 |
240 |
360 |
· Code Review |
· 代码复审 |
25 |
30 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
40 |
60 |
Reporting |
报告 |
35 |
40 |
· Test Report |
· 测试报告 |
20 |
20 |
· Size Measurement |
· 计算工作量 |
15 |
25 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
20 |
30 |
合计 |
800 |
1025 |
七、遇到的困难及解决方法:
不知如何表示空行、注释行、代码行,通过百度、CSDN学习了正则表达式,问题已解决。
八、项目小结:
通过这次练习,我对软件工程有了进一步的了解,通过PSP表格前后对比,发现我还需要学习的知识点还很多,缺乏项目经验。