一、代码
Github地址:https://github.com/lyjkekeke/WC
二、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
· Estimate | · 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 1160 | 830 |
· Analysis | · 需求分析 | 60 | 40 |
· Design Spec | · 生成设计文档 | 40 | 30 |
· Design Review | · 设计复审 | 30 | 30 |
· Coding Standard | · 代码规范 | 10 | 10 |
· Design | · 具体设计 | 100 | 60 |
· Coding | · 具体编码 | 800 | 600 |
· Code Review | · 代码复审 | 60 | 30 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 30 |
Reporting | 报告 | 120 | 90 |
· Test Report | · 测试报告 | 60 | 60 |
· Size Measurement | · 计算工作量 | 20 | 15 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 40 | 15 |
合计 | 1310 | 950 |
三、解题思路
1.首先,因为我本身还不是很了解做项目的一些流程,所以开始编程前,我首先阅读了老师推荐的书目《构建之法》的前三章,在确实了解了做项目需要的准备工作和进行流程后,我才开始进行这个作业
2.在此之后,我首先仔细浏览了该项目所要求实现的功能,并且开始考虑自己要怎么样来构建这个程序的结构以及如何依次实现这些功能,并且在PSP表格的帮助下开始计划自己实现该程序的时间。
3.在做好准备工作后,我心里也有了一个对于这个程序内容的大致框架,这时候我就开始编程并且一步步实现需求分析里所要求的功能,从基本功能开始,再到扩展功能和高级功能。过程中也出现许多我不了解的知识,这时候我就通过上网查找资料来解决自己所遇到的一些问题。
4.在进行了单元测试,回归测试等测试后,我完成了这个程序,但还有许多不足,我在之后的时间里会一步步改进这个程序。
四、设计实现过程
在我的设计中,该程序主要包含几个方面:检查用户输入,对用户的输入进行解析,实现相应的功能。
我主要设置了四个类,主类(wc_main),返回功能数据的类(r_data),实现文件递归获取的类(getFile),以及实现图形化界面的类(selectFile)。
主要结构如下图所示:
我首先在主类wc_main中设置了检查用户输入的方法judge()和一个检测用户输入文件是否存在的方法fnameJudge(),然后根据检测到的结果调用r_data类中的方法,其中检测到-c即调用获取字符数方法r_character(),检测到-w即调用获取单词数方法r_word(),检测到-l即调用获取行数的方法r_line(),检测到-a即调用获取代码行/空行/注释行数的方法r_other(),检测到-s后分情况考虑,若其后有-a则先调用r_file()方法获取符合条件的文件列表,然后再将每个文件调用r_other(),若只有-s则输出符合条件的文件名,若检测到-x则会弹出图形界面,在图形界面中可以选择单个文件并在界面中显示所选文件的所有数据。
-c的实现
//返回文件字符数 public int r_charactor(File file) { int rData=0; Reader r_file = null; try { r_file = new InputStreamReader(new FileInputStream(file)); while((r_file.read()) != -1) { if((char)r_file.read() != '\r' && (char)r_file.read() != '\n') { rData++; } } r_file.close(); } catch(Exception e) { System.out.println("指定输入文件不存在"); } return rData; }
在这里我使用字符流通过读入字符的同时计数实现获取字符数
-w的实现
//返回文件单词数 public int r_word(File file) { int rData=0; BufferedReader r_file = null; String str; try { r_file = new BufferedReader(new InputStreamReader(new FileInputStream(file))); while((str = r_file.readLine()) != null) { Pattern pattern = Pattern.compile("\\b[a-zA-Z]+\\b"); Matcher matcher=pattern.matcher(str); while(matcher.find()){ rData++; } } r_file.close(); } catch(Exception e) { System.out.println("指定输入文件不存在"); } return rData; }
在这里我通过识别一个字母加一个非字母来判定单词的个数
-l的实现
//返回文件行数 public int r_line(File file) { int rData=0; BufferedReader r_file = null; try { r_file = new BufferedReader(new InputStreamReader(new FileInputStream(file))); while((r_file.readLine()) != null) { rData++; } r_file.close(); } catch(Exception e) { System.out.println("指定输入文件不存在"); } return rData; }
在逐行读取文件内容的同时计数
-a的实现
//返回代码行/空行/注释行 public oData r_other(File file) { oData odata = new oData(); BufferedReader r_file = null; boolean tag = false; int rLine = 0,rNode=0,rEmpty=0,rNote=0; try { r_file = new BufferedReader(new InputStreamReader(new FileInputStream(file))); String str = null; while((str = r_file.readLine()) != null) { rLine++; str = str.replaceAll("\r\n"," "); //多行注释中统计 if(tag == true) { ++rNote; if(str.endsWith("*/")){ tag = false;//多行注释在本行结束 } } if(str.startsWith("/*") || str.startsWith("{/*")) { if(str.endsWith("*/") ) { ++rNote; } else { ++rNote; tag = true; } } //空白行统计 if(str.matches("\\s*") || str.equals("{") || str.equals("}")){ ++rEmpty; } //单行注释统计 if(str.startsWith("//") || str.startsWith("{//")) { ++rNote; } rNode = rLine - rNote - rEmpty; } r_file.close(); } catch(Exception e) { System.out.println("指定输入文件不存在"); } odata.rNote = rNote; odata.rNode = rNode; odata.rEmpty = rEmpty; return odata; }
通过识别特殊的格式来计数
-s的实现
//递归获取文件 public void r_file(String path,String flag){ String type = null; List<File> filelist; File file = new File(path); path = file.getAbsolutePath(); file = new File(path); if(file.isDirectory()) { System.out.println("您想要搜索的格式为(例:.txt):"); //获取用户输入的信息 try { BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(System.in)); type = bufferedReader.readLine(); bufferedReader.close(); } catch(IOException e) { e.printStackTrace(); } } else { type = path.substring(path.lastIndexOf(".")); path = file.getParent(); } filelist = getFile.getFileList(path,type); if(flag == "n") { System.out.println("符合条件的文件有:"); for(int i=0;i<filelist.size();i++) { String strFileName = filelist.get(i).getAbsolutePath(); System.out.println(i+1 + ". " + strFileName); } } else if(flag == "a"){ System.out.println("符合条件的文件及其结果如下:"); for (int i=0;i<filelist.size();i++) { String strFileName = filelist.get(i).getAbsolutePath(); System.out.println(i+1 + " " + strFileName); r_data rd = new r_data(); System.out.println("字符数:"+rd.r_charactor(filelist.get(i))); System.out.println("单词数:"+rd.r_word(filelist.get(i))); System.out.println("行数:"+rd.r_line(filelist.get(i))); oData odata = new oData(); odata = rd.r_other(file); System.out.println("该文件中含有:"); System.out.println("代码行:" + odata.rNode); System.out.println("空白行:" + odata.rEmpty); System.out.println("注释行:" + odata.rNote); } } }
public static List<File> getFileList(String filePath,String type) { File file = new File(filePath); File[] files = file.listFiles(); // 该文件目录下文件全部放入数组 if (files != null) { for (int i = 0; i < files.length; i++) { String fileName = files[i].getName(); if (files[i].isDirectory()) { // 判断是文件还是文件夹 getFileList(files[i].getAbsolutePath(),type); // 获取文件绝对路径 } if (fileName.endsWith(type)) { // 判断文件名是否是用户想要的格式 filelist.add(files[i]); } } } return filelist; }
首先写好递归获取符合要求的文件目录的方法,然后根据条件的不同来调用它
-x的实现
public class selectFile extends JFrame implements ActionListener{ private static final long serialVersionUID = 1L; JButton btn1 = null; JButton btn2 = null; JTextField textField = null; JTextArea textArea = null; public selectFile() { this.setTitle("选择文件"); this.setSize(300,100); FlowLayout layout = new FlowLayout(); JLabel label = new JLabel("请选择文件:"); textField = new JTextField(30); textArea = new JTextArea(30,30); textArea.setLineWrap(true); JScrollPane jsp = new JScrollPane(textArea); btn1 = new JButton("选择"); // 设置布局 layout.setAlignment(FlowLayout.LEFT);// 左对齐 this.setLayout(layout); this.setBounds(400, 400, 600, 100); this.setVisible(true); this.setResizable(false); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); btn1.addActionListener(this); add(label); add(textField); add(btn1); add(textArea); } @Override public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser(); chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); chooser.showDialog(new JLabel(), "选择"); File file = chooser.getSelectedFile(); textField.setText(file.getAbsoluteFile().toString()); r_data rd = new r_data(); oData odata = new oData(); odata = rd.r_other(file); textArea.append("该文件中含有:" + "\r\n"); textArea.append("代码行:" + odata.rNode + "\r\n"); textArea.append("空白行:" + odata.rEmpty + "\r\n"); textArea.append("注释行:" + odata.rNote + "\r\n"); textArea.append("字符数:"+rd.r_charactor(file) + "\r\n"); textArea.append("单词数:"+rd.r_word(file) + "\r\n"); textArea.append("行数:"+rd.r_line(file) + "\r\n"); } }
public class selectFile extends JFrame implements ActionListener{ private static final long serialVersionUID = 1L; JButton btn1 = null; JButton btn2 = null; JTextField textField = null; JTextArea textArea = null; public selectFile() { this.setTitle("选择文件"); this.setSize(300,100); FlowLayout layout = new FlowLayout(); JLabel label = new JLabel("请选择文件:"); textField = new JTextField(30); textArea = new JTextArea(30,30); textArea.setLineWrap(true); JScrollPane jsp = new JScrollPane(textArea); btn1 = new JButton("选择"); // 设置布局 layout.setAlignment(FlowLayout.LEFT);// 左对齐 this.setLayout(layout); this.setBounds(400, 400, 600, 100); this.setVisible(true); this.setResizable(false); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); btn1.addActionListener(this); add(label); add(textField); add(btn1); add(textArea); } @Override public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser(); chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); chooser.showDialog(new JLabel(), "选择"); File file = chooser.getSelectedFile(); textField.setText(file.getAbsoluteFile().toString()); r_data rd = new r_data(); oData odata = new oData(); odata = rd.r_other(file); textArea.append("该文件中含有:" + "\r\n"); textArea.append("代码行:" + odata.rNode + "\r\n"); textArea.append("空白行:" + odata.rEmpty + "\r\n"); textArea.append("注释行:" + odata.rNote + "\r\n"); textArea.append("字符数:"+rd.r_charactor(file) + "\r\n"); textArea.append("单词数:"+rd.r_word(file) + "\r\n"); textArea.append("行数:"+rd.r_line(file) + "\r\n"); } }
主要是通过容器JFrame来实现界面,然后通过JFileChooser来选择文件
我测试过了以下类型文件:空文件、只有一个字符的文件、只有一个词的文件、只有一行的文件、一个典型的源文件
下面是一个典型源文件的测试结果:
典型源文件内容为:
测试结果为:
注:因为结果过长只截取了一部分
五、总结
在这一次的作业中,我基本实现了基本功能、拓展功能和高级功能,但是拓展功能里通配符?并没有实现,而且因为代码结构的原因我有许多想要将功能结合的点没有实现,我认为这是我前期设计时的失误,在下一次的编程中将会更加认真的去计划和设计程序,做好充足的准备工作。另外目前这个代码我会找机会将它写的更加完善。通过这次练习,我更加熟悉了io流读写,也更加了解了正则表达式,写起代码来也更加得心应手了,总的来说,这样练手很有益处。