GitHub项目地址:https://github.com/593369243/wc
1.项目要求
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 返回更复杂的数据(代码行 / 空行 / 注释行)。
2.解题思路
刚开始拿到题目的时候不理解项目功能要求里说的程序处理用户需求的模式,后来去百度上看了一些别人完成的例子才明白。因为不会读取文件内容,所以去学习了一些I/O流方面的知识。
统计字符数:使用文件输入流一次读取一个字符,若不为换行符则字符数+1
统计词数:使用文件输入流一次读取一个字符,若当前字符为字母但前一个字符不是字母,将flag置为true; 若当前字符不为字母且前一个字符是字母,将flag置为false,单词数+1
统计行数:使用字符缓冲流一次读取一个文本行,若不为null则行数 +1
统计代码行:使用字符缓冲流一次读取一个文本行,若字符数大于1则代码行数 +1
统计注释行:使用字符缓冲流一次读取一个文本行,若包含"//"或“/*”则注释行数+1并置flag为true,当文本行包含“*/”且flag为true,则注释行数+1并置flag为false
统计空行:使用字符缓冲流一次读取一个文本行,字符数为0或1则空行数+1
3.设计实现过程
有两个类,四个功能加上主函数一共五个函数,其中实现统计功能的函数放在count类中,主函数在HelloWorld类中。在主函数中通过静态调用count类中实现功能的四个函数分别实现四个功能。
4.代码说明
- 统计字符数
public static void countchar(String filename) { int charnum = 0; // 字符数量 String filepath = "./text/" + filename; // 文件相对路径 try (FileReader in = new FileReader(filepath)) { // 创建文件输入流 int ch = in.read(); while (ch != -1) { // ch=-1则已到达流末尾 if (ch != '\n') { // 读取字符不为换行符则字符数+1 charnum++; } ch = in.read(); } System.out.println("该文件包含字符数:" + charnum); } catch (IOException e) { System.out.println("该文件不存在,请重新输入命令"); } }
- 统计单词数
public static void countword(String filename) { int wordnum = 0; // 单词数量 String filepath = "./text/" + filename; // 文件相对路径 try (FileReader in = new FileReader(filepath)) { // 创建文件输入流 int ch = in.read(); boolean flag = false; // 判断是前一个字符是否为字母 while (ch != -1) { // ch=-1则已到达流末尾 if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '-') { if (!flag) { flag = true; // 当前字符为字母但前一个字符不是字母,将flag置为true } } else { if (flag) { // 当前字符不为字母且前一个字符是字母,将flag置为false,单词数+1 wordnum++; flag = false; } } ch = in.read(); } if (flag) { // 以单词结尾需要将单词数+1 wordnum++; } System.out.println("该文件包含单词数:" + wordnum); } catch (IOException e) { System.out.println("该文件不存在,请重新输入命令"); } }
- 统计行数
public static void countline(String filename) { int linenum = 0; // 行数 String filepath = "./text/" + filename; // 文件相对路径 // 创建字符缓冲流 try (FileReader in = new FileReader(filepath); BufferedReader bin = new BufferedReader(in)) { while (bin.readLine() != null) { // bin.readLine()返回值为空则已到达流末尾 linenum++; // 读取一个文本行不为空则行数+1 } System.out.println("该文件包含行数:" + linenum); } catch (IOException e) { System.out.println("该文件不存在,请重新输入命令"); } }
- 统计空格数、注释行数、代码行数
public static void complex(String filename) { int blanknum = 0; // 空行数 int annotationnum = 0; // 注释行数 int codenum = 0; // 代码行数 boolean flag = false; // 判断是否处于多行注释中 String filepath = "./text/" + filename; // 文件相对路径 // 创建字符缓冲流 try (FileReader in = new FileReader(filepath); BufferedReader bin = new BufferedReader(in)) { String str = bin.readLine(); // 读取一个文本行 while (str != null) { // str为空则已到达流末尾 if (str.contains("/*")) { // 多行注释开头,置flag为true,注释行数+1 flag = true; annotationnum++; } else if (flag) { // flag为true,处于多行注释中,注释行数+1 annotationnum++; if (str.contains("*/")) { // 多行注释结尾,置flag为false flag = false; } } else if (str.contains("//")) { // 单行注释,注释行数+1 annotationnum++; } else if (str.length() > 1) { // 不为注释行且长度大于1为代码行 codenum++; } else { blanknum++; // 空行 } str = bin.readLine(); } System.out.println("该文件包含空行数:" + blanknum); System.out.println("该文件包含代码行数:" + codenum); System.out.println("该文件包含注释行数:" + annotationnum); } catch (IOException e) { System.out.println("该文件不存在,请重新输入命令"); } }
- 主函数
public static void main(String[] args) { // 输入提示界面 System.out.println("输入介绍:wc.exe [parameter] [file_name]"); System.out.println("wc.exe -c file.c 返回文件 file.c 的字符数"); System.out.println("wc.exe -w file.c 返回文件 file.c 的单词数 "); System.out.println("wc.exe -l file.c 返回文件 file.c 的行数"); System.out.println("wc.exe -a file.c 返回更复杂的数据(空行数/代码行数/注释行数)"); System.out.println("请输入命令:"); boolean flag = true; while (flag) { Scanner scan = new Scanner(System.in); // 获取命令 String cmd = scan.nextLine(); if (cmd.startsWith("wc.exe")) { // 判断输入的命令格式是否正确 cmd = cmd.substring(7); //截取指令 switch (cmd.substring(0, 2)) { case "-c": // 统计字符数 cmd = cmd.substring(3); count.countchar(cmd); break; case "-w": // 统计单词数 cmd = cmd.substring(3); count.countword(cmd); break; case "-l": // 统计行数 cmd = cmd.substring(3); count.countline(cmd); break; case "-a": // 统计空行数、代码行数、注释行数 cmd = cmd.substring(3); count.complex(cmd); break; default: System.out.println(cmd.substring(0, 1)); System.out.println(cmd); System.out.println("请输入正确的命令"); continue; } } else if (cmd.equalsIgnoreCase("exit")) { // 输入exit退出 flag = false; } else { System.out.println("请输入正确的命令"); continue; } } }
5.测试运行
- 空文件
- 只有一个字符的文件
- 只有一个词的文件
- 只有一行的文件
- 典型的源文件
6.PSP表格
psp2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 30 | 40 |
· Estimate | · 估计这个任务需要多少时间 | 30 | 40 |
Development | 开发 | 370 | 680 |
· Analysis | · 需求分析 (包括学习新技术) | 40 | 90 |
· Design Spec | · 生成设计文档 | 20 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 10 | 20 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 50 |
· Design | · 具体设计 | 40 | 100 |
· Coding | · 具体编码 | 150 | 240 |
· Code Review | · 代码复审 | 30 | 50 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 100 |
Reporting | 报告 | 60 | 120 |
·Test Report | · 测试报告 | 40 | 80 |
· Size Measurement | · 计算工作量 | 10 | 20 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 10 | 20 |
合计 | 460 | 840 |
7.项目小结
因为java只学了一部分,基础知识没有完全理解,很多方法都不熟悉,很多知识都没有深入了解,做起来有点手忙脚乱 ,结果完成项目的实际耗时远多于自己估计的时间,拓展功能也没能实现,很大一部分原因是自己的编程能力不足。通过这次项目的练习,学习了如何使用输入流读取文件内容,也进一步熟悉了写博客的一些技巧和上传项目到Github的方法。希望以后可以少点拖拉,思考的更快些。