个人项目wordCount
项目的Github地址:https://github.com/KenLoong/wc.git
一、项目简介
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 返回更复杂的数据(代码行 / 空行 / 注释行)。
空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。
代码行:本行包括多于一个字符的代码。
注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释:
} //注释
在这种情况下,这一行属于注释行。
二、PSP表格
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
30 |
30 |
· Estimate |
· 估计这个任务需要多少时间 |
120 |
410 |
Development |
开发 |
90 |
180 |
· Analysis |
· 需求分析 (包括学习新技术) |
30 |
60 |
· Design Spec |
· 生成设计文档 |
10 |
10 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
30 |
30 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
20 |
10 |
· Design |
· 具体设计 |
30 |
30 |
· Coding |
· 具体编码 |
20 |
20 |
· Code Review |
· 代码复审 |
10 |
30 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
60 |
120 |
Reporting |
报告 |
20 |
20 |
· Test Report |
· 测试报告 |
10 |
10 |
· Size Measurement |
· 计算工作量 |
10 |
30 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
10 |
10 |
合计 |
|
330 |
410 |
三、解题思路
刚看到题目时,我就想到了要用字符流来读取文件的内容,然后再进行逻辑判断来计算结果。首先是一定要有文件路径的参数,根据这个参数来操作文件。因为跟字符打交道,我设想用StringBuilder这些字符串的包装类来接收文件的内容,再根据它特有的方法来对文本内容进行解析。由于要统计单词,我想要用正则表达式来进行分割。
四、设计实现过程
根据每个需求来创建一个方法来实现功能,所以一共有5个方法,分别来实现-a,-w,-l,-s,-a的功能。只有一个类,这5个方法通过主方法来调用 。
五、代码说明
主方法来接收参数,然后调用其他方法
private static String func;//功能参数 private static String filePath;//文件路径 public static void main(String[] args) throws Exception { String s=""; while (true) { Scanner scanner = new Scanner(System.in); if (scanner.hasNextLine()) s = scanner.nextLine(); //-s C:\\Users\\win10\\Desktop\\测试 args = s.split("\\s+"); //判断是否有足够的参数 if (args.length < 2) { System.out.println("请输入足够的参数..."); continue; } func = args[0]; filePath = args[1]; //判断文件是否存在 File file = new File(filePath); if (!file.exists()) { System.out.println("你指定的文件不存在...."); continue; } switch (func) { case "-w": countWord(null, filePath); break; case "-c": countChar(null, filePath); break; case "-l": countLine(null, filePath); break; case "-s": countDir(filePath); break; case "-a": countComplex(filePath); break; default: System.out.println("您输入的参数有误"); } } }
统计行数:用BufferedReader来一行一行的读取文件。
public static void countLine (String fileName,String absolutePath) throws Exception { BufferedReader br=new BufferedReader(new FileReader(new File(absolutePath))); String line; int sum = 0; while((line=br.readLine())!=null) { sum++; } System.out.println((fileName==null?"":fileName)+"文件行数为:"+sum); }
统计字符:一行一行的读取,累加长度
public static void countChar(String fileName,String absolutePath) throws Exception { BufferedReader br=new BufferedReader(new FileReader(new File(absolutePath))); int sum = 0 ; String line; while ((line = br.readLine()) != null){ sum+=line.length(); } System.out.println((fileName==null?"":fileName)+"文件字符数为:"+sum); }
统计单词数:用正则表达式来分割
public static void countWord(String fileName,String absolutePath) throws Exception { BufferedReader br=new BufferedReader(new FileReader(new File(absolutePath))); int sum = 0 ; String line; String reg = "[\\s]+"; while((line=br.readLine())!=null) { line = line.replaceAll("[/*{}\\.]","");//去掉注释符号及其他字符 String[] split = line.split(reg); if (!"".equals(split[0]))//排除空行影响 sum += split.length ; } System.out.println((fileName==null?"":fileName)+"文件单词数为:"+sum); }
复杂统计:统计代码行,注释行,空行
public static void countComplex(String filePath) throws IOException { int spaceNum = 0;//空行 int annoNum = 0;//注释行 int codeNum = 0;//代码行 BufferedReader br=new BufferedReader(new FileReader(new File(filePath))); String line; String reg = ""; while ( (line = br.readLine()) != null){ String s = line.replaceAll("[\\s;]", ""); if (s.length() == 0){//空行 spaceNum++; }else if (s.length() == 1 && ("{".equals(s) || "}".equals(s)) ){//单字符空行 spaceNum++; }else if ( (s.startsWith("{") || s.startsWith("}") ) && s.contains("//")){//单字符的注释行 annoNum++; }else if (s.startsWith("//")){//单行注释 annoNum++; }else if (s.startsWith("/*") && s.length()>=4 && s.endsWith("*/")){//单行注释 annoNum++; }else if (s.startsWith("/*")){//多行注释 annoNum++; while (true){ if ((line = br.readLine()) != null){ annoNum++; if (line.endsWith("*/"))break;//多行注释结束 }else { break; } } }else { codeNum++;//代码行 } } System.out.println("文件空行数为:"+spaceNum); System.out.println("文件注释行数为:"+annoNum); System.out.println("文件代码行数为:"+codeNum); }
递归统计目录的文件(默认是统计代码行,注释行,空行)
public static void countDir(String filePath) throws Exception { File dir = new File(filePath); if (!dir.isDirectory() || !dir.exists()){ System.out.println("你指定的不是目录"); return; } File[] files = dir.listFiles(); for (File file : files) { //还有目录,再递归 if (file.isDirectory()){ countDir(file.getAbsolutePath()); }else { String name = file.getName(); String absolutePath = file.getAbsolutePath(); //调用其他三个方法 countLine(name,absolutePath); countChar(name,absolutePath); countWord(name,absolutePath); } } }
六、测试
空文件
单行文件
一个词文件
源文件
处理目录的文件
七,项目小结
开发的时间要比我预计的要多很多,可以说是我经验还是太少了。测试也用去了不少时间,说明我的代码安全性考虑得不够好。通过这次项目,我认识到了自己的不足,也学到了新技术,例如如何将java项目转成exe文件。正则表达式以前学过,但这次项目要用到时我还是忘了,又要去查资料,看来我需要多做笔记。