try (InputStream is = new FileInputStream(file)) { StringBuffer sb = new StringBuffer(); XWPFDocument xp = new XWPFDocument(is); XWPFWordExtractor wordReader = new XWPFWordExtractor(xp); String[] line = wordReader.getText().split("\n"); if (line.length < 1 || line == null) { return null; } for (int j = 0; j < line.length; j++) { System.out.println(line[j]); lineNum += 1; if (line[j].trim().toString().length() <= 1) { spaceLineNum += 1; continue; } String[] result = calcuChar(line[j]).split("=="); wordNum += Integer.parseInt(result[0]); charNum += Integer.parseInt(result[1]); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
对于注释行的计算逻辑:
这里的注释指: // 和 /**/;对于 // 的校验相对简单,关键在于对 /**/的校验;这里flag就发挥了作用,即在前面的行扫描中是否遇到了/*.如果该次扫描检测到该行存在/*,flag则变为true,这样在一定程度上对后面扫描到的* 和*/能有一定的可鉴程度,当扫面到*或者*/时,flag为true代表改行为注释行,否则为代码或者空行,在尚未能够判断遇到的行是否为注释行时,temp负责存储存在疑问的行数,待能确定flag的值时,相当于计时器的存在.
boolean flag = false; int temp = 0; while ((line = br.readLine()) != null) { System.out.println("该行内容为:" + line); lineNum += 1; System.out.println(); if (line.trim().length() <= 1) { spaceLineNum += 1; continue; } if (line.trim().contains("//")) { annotationNum += 1; continue; } if (line.trim().contains("/*")) { temp += 1; flag = true; } if (!line.trim().contains("/*") && !line.trim().contains("*/") && line.trim().contains("*")) { if (flag) { temp += 1; } } if (line.trim().contains("*/")) { if (flag) { flag = false; annotationNum = temp + 1; temp = 0; if (lineNum > annotationNum) { lineNum = lineNum - annotationNum; } continue; } } }
字符的计算
这里对字符的计算进行封装,采取的策略是一行一行的计算字符,最后因为返回值限制,故而返回字符串形式,"词数==字符数",这里双等号的意义是为方便split方法.
public static String calcuChar(String line) { int wordNum = 0; int charNum = 0; for (int i = 0; i < line.split("\\s+").length; i++) { String tempWord = line.split("\\s+")[i].trim(); if ("".equals(tempWord) || tempWord == null) { continue; } if (pattern.matcher(tempWord).find()) { char c[] = tempWord.toCharArray(); //汉字前一个字母是否已经被算入 boolean flag = true; if (pattern.matcher(String.valueOf(c[0])).matches()) { wordNum += 1; charNum += 2; flag = false; } else { charNum += 1; } for (int k = 1; k < c.length; k++) { if (pattern.matcher(String.valueOf(c[k])).matches()) { if (flag) { wordNum += 1; flag = false; } wordNum += 1; charNum += 2; } else { flag = true; charNum += 1; } } if (flag) { wordNum += 1; } } else { wordNum += 1; charNum += line.split("\\s+")[i].trim().length(); } } return wordNum + "==" + charNum; }
图形界面的构建
最初采用hashmap存取组件来实现更新时获取对应组件进行更新.但后面因加入了多线程读取多个文件的缘故,舍弃了该方法,将图形界面的构建分成三部分,代码一是主界面的构建,也算是程序启动的入口.代码二是主界面按钮的实现以及监听事件;监听事件里每次读取到一个文件就new一个线程创建该文件的显示界面,再通过代码三来实现数据的绑定;这里用到的ResultVo是因返回参数的数量较多,故而将这些属性封装成一个类,参考代码四
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public static void main(String[] args) { JFrame frame = new JFrame("主页面"); frame.setSize(350, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel(); frame.add(panel); placeComponents(panel); // 设置界面可见 frame.setVisible(true); }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
private static void placeComponents(JPanel panel) { panel.setLayout(null); JLabel userLabel = new JLabel("请选择文件:"); userLabel.setBounds(10, 20, 80, 25); JLabel findFiles = new JLabel("递归回显:"); findFiles.setBounds(10, 60, 80, 25); panel.add(userLabel); panel.add(findFiles); // 递归读文件 JButton jButton2 = new JButton("请选择文件"); jButton2.setBounds(100, 60, 100, 30); jButton2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser("C:\\Users\\hp\\Desktop"); chooser.setMultiSelectionEnabled(false); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int returnVal; returnVal = chooser.showOpenDialog(jButton2); if (returnVal == JFileChooser.OPEN_DIALOG) { System.out.println(chooser.getSelectedFile()); List<String> pathList = findFilePath(chooser.getSelectedFile().getAbsolutePath()); if (pathList != null) { JFrame frame = new JFrame("文件列表"); frame.setSize(350, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel(); frame.add(panel); frame.setVisible(true); int step = 10; for (String path : pathList) { System.out.println(path); JLabel fileName = new JLabel("文件:"+path); fileName.setBounds(10, 10+step, 80, 25); panel.add(fileName); step += 10; } } else { JOptionPane.showMessageDialog(null, "该文件夹下不存在符合条件的文件", "扫描失败", JOptionPane.ERROR_MESSAGE); } } } }); panel.add(jButton2); JButton jButton = new JButton("请选择文件"); jButton.setBounds(100, 20, 100, 30); jButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser("C:\\Users\\hp\\Desktop"); chooser.setMultiSelectionEnabled(true); int returnVal; returnVal = chooser.showOpenDialog(jButton); if (returnVal == JFileChooser.APPROVE_OPTION) { File[] files = chooser.getSelectedFiles(); for (File f : files) { if (f.getAbsolutePath().substring(f.getAbsolutePath().lastIndexOf(".") + 1).equals("docx")) { new Thread(new Runnable() { @Override public void run() { ResultVo vo = ReadUtils.readDoc(f); if (vo == null) { JOptionPane.showMessageDialog(null, "文件路径错误", "扫描失败", JOptionPane.ERROR_MESSAGE); } else { JFrame frame = new JFrame("文件信息"); frame.setSize(350, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel(); frame.add(panel); frame.setVisible(true); placeComponents(panel, vo); } } }).start(); } else if (f.getAbsolutePath().substring(f.getAbsolutePath().lastIndexOf(".") + 1).equals("txt") || f.getAbsolutePath().substring(f.getAbsolutePath().lastIndexOf(".") + 1).equals("java")) { new Thread(new Runnable() { @Override public void run() { ResultVo vo = ReadUtils.readTxt(f); if (vo == null) { JOptionPane.showMessageDialog(null, "文件路径错误", "扫描失败", JOptionPane.ERROR_MESSAGE); } else { System.out.println(1); JFrame frame = new JFrame("文件信息"); frame.setSize(350, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel(); frame.add(panel); frame.setVisible(true); placeComponents(panel, vo); } } }).start(); } else { JOptionPane.showMessageDialog(null, "暂不支持对该类型文件的扫描 ", "错误 ", JOptionPane.ERROR_MESSAGE); } } } } }); panel.add(jButton); }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
private static void placeComponents(JPanel panel, ResultVo vo) { JLabel fileName = new JLabel("文件名:" + vo.getFileName()); fileName.setBounds(10, 30, 80, 25); panel.add(fileName); JLabel charNum = new JLabel("字符数:" + vo.getCharNum()); charNum.setBounds(10, 40, 80, 25); panel.add(charNum); JLabel wordNum = new JLabel("词数:" + vo.getWordNum()); wordNum.setBounds(10, 55, 80, 25); panel.add(wordNum); JLabel lineNum = new JLabel("行数:" + vo.getLineNum()); lineNum.setBounds(10, 70, 80, 25); panel.add(lineNum); JLabel spaceLineNum = new JLabel("空行数:" + vo.getSpaceLineNum()); spaceLineNum.setBounds(10, 85, 80, 25); panel.add(spaceLineNum); JLabel annotationNum = new JLabel("注释行数:" + vo.getAnnotationNum()); annotationNum.setBounds(10, 100, 80, 25); panel.add(annotationNum); }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public class ResultVo { private int wordNum = 0; private int charNum = 0; private int lineNum = 0; private int spaceLineNum = 0; private int annotationNum = 0; private String fileName = ""; public int getWordNum() { return wordNum; } public void setWordNum(int wordNum) { this.wordNum = wordNum; } public int getCharNum() { return charNum; } public void setCharNum(int charNum) { this.charNum = charNum; } public int getLineNum() { return lineNum; } public void setLineNum(int lineNum) { this.lineNum = lineNum; } public int getSpaceLineNum() { return spaceLineNum; } public void setSpaceLineNum(int spaceLineNum) { this.spaceLineNum = spaceLineNum; } public int getAnnotationNum() { return annotationNum; } public void setAnnotationNum(int annotationNum) { this.annotationNum = annotationNum; } @Override public String toString() { return "ResultVo{" + "wordNum=" + wordNum + ", charNum=" + charNum + ", lineNum=" + lineNum + ", spaceLineNum=" + spaceLineNum + ", annotationNum=" + annotationNum + ", fileName='" + fileName + '\'' + '}'; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } }
项目测试
启动程序
多文件读取(docx,txt,java,空文件):
递归回显:
非法读取校验:
代码覆盖率:
整体覆盖率80%
Planning
|
计划
| 预估耗时(分钟 | 实际耗时(分钟) |
· Estimate
|
· 估计这个任务需要多少时间
| 180 | 240 |
Development
|
开发
| 150 | 180 |
· Analysis
|
· 需求分析 (包括学习新技术)
| 40 | 40 |
· Design Spec
|
· 生成设计文档
| 30 | 30 |
· Design Review
|
· 设计复审 (和同事审核设计文档)
| ||
· Coding Standard
|
· 代码规范 (为目前的开发制定合适的规范)
| 20 | 20 |
· Design
|
· 具体设计
| 20 | 20 |
· Coding
|
· 具体编码
| 150 | 180 |
· Code Review
|
· 代码复审
| 20 | 20 |
· Test
|
· 测试(自我测试,修改代码,提交修改)
| 60 | 60 |
Reporting
|
报告
| 100 | 100 |
· Test Report
|
· 测试报告
| 10 | 7 |
· Size Measurement
|
· 计算工作量
| 30 | 30 |
· Postmortem & Process Improvement Plan
|
· 事后总结, 并提出过程改进计划
| 10 | 10 |
合计
| 640 | 697 |