GitHub项目仓库地址:https://github.com/yyffish/WordCount
项目描述
wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有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 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。
- 基本的Windows GUI 程序操作
- 支持通过图形界面选取文件
- 支持通过图形界面展现文件的信息
- 已实现功能:
- -c
- -w
- -l
- -s
- -a
- -x
PSP
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
30 |
5 |
· Estimate |
· 估计这个任务需要多少时间 |
30 |
5 |
Development |
开发 |
620 |
615 |
· Analysis |
· 需求分析 (包括学习新技术) |
150 |
100 |
· Design Spec |
· 生成设计文档 |
45 |
5 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
15 |
5 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
20 |
5 |
· Design |
· 具体设计 |
30 |
20 |
· Coding |
· 具体编码 |
300 |
95 |
· Code Review |
· 代码复审 |
30 |
350 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
30 |
35 |
Reporting |
报告 |
65 |
85 |
· Test Report |
· 测试报告 |
30 |
70 |
· Size Measurement |
· 计算工作量 |
10 |
10 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
25 |
5 |
合计 |
|
715 |
705 |
解题思路
看完题目,了解到需要统计文件的各种数据,比如字符数,单词数,代码行等等,而且有图形界面就更好。
使用语言:使用JAVA编程,并使用Dynamic Web Project + JSP + JSTL表达式进行开发,并部署到Tomcat服务器中,以实现图形化的功能
设计方案
技术栈
该项目基于Java语言开发,使用JavaWeb配合HTML进行开发。
具体设计
采用Dao-Service-Servlet-JSP四层设计,Dao层做IO逻辑处理,Service层封装Dao层方法,Servlet层做前端数据交互和调用Service层方法,JSP层做数据展示和前端UI界面。
接口文档
fileCountDao层逻辑设计
- 根据路径返回FileCount对象 fileCountDao.count
- 初始化各个整形变量
- 建立IO流
- 计算词个数
- 封装到FileCount对象中
- 返回FileCount对象
folderDao逻辑设计
- 根据路径返回Folder对象 folderDao.getFolder
- 建立IO流
- 将获取的文件封装为fileList和folderList
- 将路径和fileList以及folderList封装到Folder对象中
- 返回Folder对象
关键代码
Folder对象
1 package com.wordcount.entity; 2 3 import java.util.List; 4 5 public class Folder { 6 private List<String> fileList; 7 private List<String> folderList; 8 private String path; 9 10 public Folder() { 11 super(); 12 } 13 14 public Folder(List<String> fileList, List<String> folderList, String path) { 15 super(); 16 this.fileList = fileList; 17 this.folderList = folderList; 18 this.path = path; 19 } 20 21 public List<String> getFileList() { 22 return fileList; 23 } 24 25 public void setFileList(List<String> fileList) { 26 this.fileList = fileList; 27 } 28 29 public List<String> getFolderList() { 30 return folderList; 31 } 32 33 public void setFolderList(List<String> folderList) { 34 this.folderList = folderList; 35 } 36 37 public String getPath() { 38 return path; 39 } 40 41 public void setPath(String path) { 42 this.path = path; 43 } 44 45 }
FileCount对象
1 package com.wordcount.entity; 2 3 public class FileCount { 4 private String fileName;// 文件名 5 private Integer charNum;// 字符数 6 private Integer lineNum;// 行数 7 private Integer wordNum;// 单词数 8 private Integer codeLineNum;// 代码行 9 private Integer emptyLineNum;// 空行 10 private Integer remarkLineNUm;// 注释行 11 12 public FileCount() { 13 super(); 14 } 15 16 public FileCount(String fileName, Integer charNum, Integer lineNum, Integer wordNum, Integer codeLineNum, 17 Integer emptyLineNum, Integer remarkLineNUm) { 18 super(); 19 this.fileName = fileName; 20 this.charNum = charNum; 21 this.lineNum = lineNum; 22 this.wordNum = wordNum; 23 this.codeLineNum = codeLineNum; 24 this.emptyLineNum = emptyLineNum; 25 this.remarkLineNUm = remarkLineNUm; 26 } 27 28 public String getFileName() { 29 return fileName; 30 } 31 32 public void setFileName(String fileName) { 33 this.fileName = fileName; 34 } 35 36 public Integer getCharNum() { 37 return charNum; 38 } 39 40 public void setCharNum(Integer charNum) { 41 this.charNum = charNum; 42 } 43 44 public Integer getLineNum() { 45 return lineNum; 46 } 47 48 public void setLineNum(Integer lineNum) { 49 this.lineNum = lineNum; 50 } 51 52 public Integer getwordNum() { 53 return wordNum; 54 } 55 56 public void setwordNum(Integer wordNum) { 57 this.wordNum = wordNum; 58 } 59 60 public Integer getCodeLineNum() { 61 return codeLineNum; 62 } 63 64 public void setCodeLineNum(Integer codeLineNum) { 65 this.codeLineNum = codeLineNum; 66 } 67 68 public Integer getEmptyLineNum() { 69 return emptyLineNum; 70 } 71 72 public void setEmptyLineNum(Integer emptyLineNum) { 73 this.emptyLineNum = emptyLineNum; 74 } 75 76 public Integer getRemarkLineNUm() { 77 return remarkLineNUm; 78 } 79 80 public void setRemarkLineNUm(Integer remarkLineNUm) { 81 this.remarkLineNUm = remarkLineNUm; 82 } 83 84 }
FolderDao.java
1 package com.workcount.dao; 2 3 import java.io.File; 4 import java.util.ArrayList; 5 import java.util.List; 6 7 import com.wordcount.entity.Folder; 8 9 public class FolderDao { 10 11 /** 12 * 根据路径返回Folder对象 13 * 14 * @param path 15 * @return 16 */ 17 public Folder getFolder(String path) { 18 Folder folder = new Folder(); 19 File dir = new File(path); 20 String[] tempList = dir.list(); 21 List<String> fileList = new ArrayList<>(); 22 List<String> folderList = new ArrayList<>(); 23 for (int i = 0; i < tempList.length; i++) { 24 String tempPath = path + tempList[i] + "/"; 25 File tempFile = new File(tempPath); 26 if (tempFile.isFile()) { 27 if (tempList[i].contains(".txt")) { 28 fileList.add(tempList[i]); 29 } 30 } else { 31 folderList.add(tempList[i]); 32 } 33 } 34 folder.setFileList(fileList); 35 folder.setPath(path); 36 folder.setFolderList(folderList); 37 return folder; 38 } 39 }
FileCountDao.java
package com.workcount.dao; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.wordcount.entity.FileCount; public class FileCountDao { /** * 根据路径返回FileCount对象 * * @param path * @return */ public FileCount count(String path) { FileCount fileCount = new FileCount(); String regxNodeBegin = "\s*/\*.*"; String regxNodeEnd = ".*\*/\s*"; String regx = "//.*"; String regxSpace = "\s*"; boolean flagNode = false; int lineNum = 0; int wordNum = 0; int charNum = 0; int codeLineNum = 0; int emptyLineNum = 0; int remarkLineNUm = 0; try { StringBuffer stringBuffer = new StringBuffer(); // 创建类进行文件的读取,并指定编码格式为utf-8 InputStreamReader read = new InputStreamReader(new FileInputStream(path), "utf-8"); // 可用于读取指定文件 BufferedReader in = new BufferedReader(read); // 定义一个字符串类型变量str String str = null; while ((str = in.readLine()) != null) {// readLine()方法, 用于读取一行,只要读取内容不为空就一直执行 lineNum++;// 每循环一次就进行一次自增,用于统计文本行数 charNum += str.length();// 用于统计总字符数 stringBuffer.append(str + ' '); if (str.matches(regxNodeBegin) && str.matches(regxNodeEnd)) { ++remarkLineNUm; continue; } if (str.matches(regxNodeBegin)) { ++remarkLineNUm; flagNode = true; } else if (str.matches(regxNodeEnd)) { ++remarkLineNUm; flagNode = false; } else if (str.matches(regxSpace)) ++emptyLineNum; else if (str.matches(regx)) ++remarkLineNUm; else if (flagNode) ++remarkLineNUm; else ++codeLineNum; } in.close();// 关闭流 Pattern pattern = Pattern.compile("\b[a-zA-Z]+\b"); Matcher matcher = pattern.matcher(stringBuffer.toString()); while (matcher.find()) { wordNum++; } } catch (Exception e) { // TODO: handle exception } fileCount.setwordNum(wordNum); fileCount.setLineNum(lineNum); fileCount.setCharNum(charNum); fileCount.setCodeLineNum(codeLineNum); fileCount.setEmptyLineNum(emptyLineNum); fileCount.setRemarkLineNUm(remarkLineNUm); fileCount.setFileName(path.substring(path.lastIndexOf("/") + 1)); return fileCount; } }
CountServlet.java
1 package com.workcount.servlet; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.annotation.WebServlet; 7 import javax.servlet.http.HttpServlet; 8 import javax.servlet.http.HttpServletRequest; 9 import javax.servlet.http.HttpServletResponse; 10 11 import com.wordcount.entity.FileCount; 12 import com.workcount.service.FileCountService; 13 14 /** 15 * Servlet implementation class CountServlet 16 */ 17 @WebServlet("/count") 18 public class CountServlet extends HttpServlet { 19 private static final long serialVersionUID = 1L; 20 21 /** 22 * @see HttpServlet#HttpServlet() 23 */ 24 public CountServlet() { 25 super(); 26 // TODO Auto-generated constructor stub 27 } 28 29 /** 30 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse 31 * response) 32 */ 33 protected void doGet(HttpServletRequest request, HttpServletResponse response) 34 throws ServletException, IOException { 35 FileCountService fileCountService = new FileCountService(); 36 FileCount fileCount = null; 37 String path = request.getParameter("path"); 38 if (path != null) { 39 fileCount = fileCountService.count(path); 40 } else { 41 fileCount = new FileCount(); 42 } 43 request.setAttribute("fileCount", fileCount); 44 request.getRequestDispatcher("count.jsp").forward(request, response); 45 } 46 47 /** 48 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse 49 * response) 50 */ 51 protected void doPost(HttpServletRequest request, HttpServletResponse response) 52 throws ServletException, IOException { 53 // TODO Auto-generated method stub 54 doGet(request, response); 55 } 56 57 }
MainServlet.java
1 package com.workcount.servlet; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.annotation.WebServlet; 7 import javax.servlet.http.HttpServlet; 8 import javax.servlet.http.HttpServletRequest; 9 import javax.servlet.http.HttpServletResponse; 10 11 import com.wordcount.entity.Folder; 12 import com.workcount.service.FolderService; 13 14 /** 15 * Servlet implementation class TestServlet 16 */ 17 @WebServlet("/main") 18 public class MainServlet extends HttpServlet { 19 private static final long serialVersionUID = 1L; 20 21 /** 22 * @see HttpServlet#HttpServlet() 23 */ 24 public MainServlet() { 25 super(); 26 // TODO Auto-generated constructor stub 27 } 28 29 /** 30 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse 31 * response) 32 */ 33 protected void doGet(HttpServletRequest request, HttpServletResponse response) 34 throws ServletException, IOException { 35 // TODO Auto-generated method stub 36 doPost(request, response); 37 } 38 39 /** 40 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse 41 * response) 42 */ 43 protected void doPost(HttpServletRequest request, HttpServletResponse response) 44 throws ServletException, IOException { 45 // TODO Auto-generated method stub 46 FolderService folderService = new FolderService(); 47 Folder folder = new Folder(); 48 String path = "C:/"; 49 if (request.getParameter("path") != null) { 50 path = request.getParameter("path") + "/"; 51 } 52 folder = folderService.getFolder(path); 53 request.setAttribute("folder", folder); 54 request.getRequestDispatcher("/table.jsp").forward(request, response); 55 } 56 57 }
运行测试
- 将WordCount项目部署到Tomcat中
- 网页首页:向本地8080端口的WordCount项目发送main请求,即可得到本机C盘的文件路径
- 打开2345Downloads文件夹,可以查看到txt文件。
- 点击11.txt文件即可查询相关信息,其中文本如下
项目总结
对于我个人来说,开发经验不足,本来是想用spring boot进行开发的,但是对spring boot使用经验不够对,有很多问题无法解决,因此选择用JavaWeb进行开发,对于其他同学,开发时间还是比较长的,遇到的问题主要还是通过百度解决,主要话费的时间在前端上,后端代码还是比较简单的。