第二周个人作业WordCount
GitHub项目地址 https://github.com/husterC/WordCount
一.PSP表格
PSP2.1 |
PSP阶段 |
预估耗时 (分钟) |
实际耗时 (分钟) |
Planning |
计划 |
15 | 15 |
· Estimate |
· 估计这个任务需要多少时间 |
5 | 5 |
Development |
开发 |
||
· Analysis |
· 需求分析 (包括学习新技术) |
60 | 70 |
· Design Spec |
· 生成设计文档 |
- | - |
· Design Review |
· 设计复审 (和同事审核设计文档) |
- | - |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
- | - |
· Design |
· 具体设计 |
60 | 60 |
· Coding |
· 具体编码 |
240 | 300 |
· Code Review |
· 代码复审 |
60 | 120 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
60 | 90 |
Reporting |
报告 |
- | - |
· Test Report |
· 测试报告 |
- | - |
· Size Measurement |
· 计算工作量 |
15 | 10 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
20 | 30 |
合计 |
8h55min | 11h40min |
二.解题思路
对于基本功能而言:
1.首先需使用Java数据输入输出流相关类对文件进行读写。
2.编写函数实现各个指令中对所读取文件的操作,返回结果数值。
3.需要使用main函数中的args参数来传递指令,对应不同指令执行不同操作。
对于扩展功能而言:
1.对于-s指令,完成函数:检查当前地址,如果是文件,就直接进行相应操作,如果是文件夹,则遍历该文件夹,对该文件夹目录下的所有内容进行递归调用处理。
2.对于-a指令,主要是注释行和代码行的区分会产生问题,先利用换行划分为不同的行,然后取注释行的flag,如果满足要求,则为注释行,如果不满足要求,则为代码行。
3.对于-e指令,先读取相应文件中的内容并划分为停用词和单词,再来判断单词是否与停用词相等,对于文件中每一个与停用词相同的单词,单词计数减一。
4.对于*匹配的情况,获取*外的文件部分作为判断标准,获取文件前面的部分作为地址,或是取项目的默认地址。然后识别满足要求的文件,对满足条件的文件进行指令操作。
三.程序设计实现过程
(补充)
程序设计说明:整个程序我是按照功能模块来划分的函数,也就是说不同指令对应是由不同的函数实现,最后在main函数中获取指令内容,然后进行指令判断,如果存在某个指令,比如-l,那么就调用实现-l功能的函数执行统计行数的功能,如此进行,待到遍历完所有命令内容后,同时统一完成输出。
具体函数与重要变量及其功能如下:
public static ArrayList<String> Path = new ArrayList<String>(); //用于存放操作文件路径
public static ArrayList<String> Name = new ArrayList<String>(); //用于存放操作文件名
public static int c(String FilePath){ //实现读取字符个数的功能
输入文件路径,返回该文件中所含有的字符数
public static int w(String FilePath){ //实现读取单词个数的功能
输入文件路径,返回该文件中所含有的单词数
public static int l(String FilePath){ //实现读取文件行数功能
输入文件路径,返回该文件中所含有的行数
public static int[] a(String FilePath){ //实现读取空行,代码行,和注释行行数的功能。
输入文件路径,返回int型数组,其中包含空行,代码行,和注释行的行数
public static int e(String FilePath,String StopFile){ //实现读取除停用词以外单词的个数的功能
输入目标文件路径和停用词文件的文件路径,返回除停用词外单词个数
public static boolean output(String Str,String FilePath){ //实现输出字符串到指定文件的功能
输入字符串和输出文件的路径,返回一个布尔值用于表示输出是否成功
public static void showFile(String NameMatch,String FilePath){ //实现带*文件的匹配和遍历文件夹的功能
输出需匹配的文件部分名称和相应路径,函数中会将符合条件的文件名称添加到全局变量Name动态数组中,路径添加到全集变量Path动态数组中
public static void main(String args[]) //主函数,用于对指令进行判断后调用函数执行相应功能
(补充)
main函数流程图如下:
三.关键部分代码
实现读取空行,代码行,和注释行功能代码
public static int[] a(String FilePath){ //实现读取空行,代码行,和注释行的功能。 int result = 1; int code = 0,emp = 0,note = 0; boolean empline = true; //空行为true boolean codeline = false; //代码行为true boolean starnote = false; //星号注释的判断,注释行为true boolean starend = false; //用于注释尾部判断 boolean starstart = false; //用于注释头部判断 boolean dounote = false; //双斜杠类型注释判断 File f = new File(FilePath); DataInputStream dis = null; if(!f.isFile() && f.exists()){ int[] a = {code,emp,note}; return a; } try { /*创建二进制输入流*/ dis = new DataInputStream(new FileInputStream(f)); /*循环读取并输出信息*/ int temp = 0; while((temp = dis.read())!=-1){ if(temp == '\n'){ if(empline == true) emp++; else if(codeline == true) code++; else note++; empline = true; codeline = false; dounote = false; } else if(temp == ' ' || temp == '\t' || temp == '\r' || temp == '{') ; else if(codeline == false){ empline =false; if(starnote == true){ if(temp == '*') //为*将标志starend置位true starend = true; else if(temp == '/' && starend == true){ //上一次为*,且这一次为/,则将starnote置位false starnote = false; starend = false; } else starend = false; } else if(temp == '/'){ if(starstart == true) //两次//且前面没有有效字符表示当前行注释 dounote = true; else starstart = true; } else if(temp == '*'){ if(starstart == true) //上次为/ 这次为*,表示注释开始 starnote = true; else codeline = true; //否则为代码行 } else if(temp == '}') ; else if(dounote == false) codeline = true; } } if(empline == true) emp++; else if(codeline == true) code++; else note++; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ if (dis!=null) { try { dis.close(); } catch (IOException e) { e.printStackTrace(); } } } int[] a = {code,emp,note}; return a; }
除去停用词统计字数函数代码
public static int e(String FilePath,String StopFile){ //实现读取除停用词以外单词的个数的功能 int result = 0; String a = ""; String[] stopword = new String[10]; for(int m = 0;m < 10;m++){ stopword[m] = ""; } File f = new File(FilePath); File Stop = new File(StopFile); DataInputStream dis_1 = null; DataInputStream dis_2 = null; if(!f.isFile() && f.exists()) return -1; if(!Stop.isFile() && Stop.exists()) return -1; try { /*创建二进制输入流*/ dis_1 = new DataInputStream(new FileInputStream(f)); dis_2 = new DataInputStream(new FileInputStream(Stop)); /*循环读取并输出信息*/ int S = 0,i = 0; while((S = dis_2.read())!=-1){ if(S != ' ') stopword[i] = stopword[i] + (char)S; else i++; } int temp = 0,j = 0; while(temp != -1 ){ //第一个循环跳过空格 换行 逗号等 第二个循环跳过正常字符 while((temp = dis_1.read())!=-1 && (temp == '\n' || temp == '\t' || temp == ' ' || temp == ',' || temp == '\r')) a = ""; if(temp == -1) //当最后一次以空格结尾时,-1表示不计算本次 result--; else a = a + (char)temp; //每当经过一次两个循环时,意味着单词数+1 while((temp = dis_1.read())!=-1 && temp != '\n' && temp != '\t' && temp != ' ' && temp != ',' && temp != '\r') a = a + (char)temp; result++; for(j = 0;j <= i;j++){ if(stopword[j].equals(a)){ result--; break; } } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ if (dis_1!=null) { try { dis_1.close(); } catch (IOException e) { e.printStackTrace(); } } if (dis_2!=null) { try { dis_2.close(); } catch (IOException e) { e.printStackTrace(); } } } return result; }
实现遍历文件夹,匹配文件功能的函数代码
public static void showFile(String NameMatch,String FilePath){ //实现带*文件的匹配和遍历文件夹的功能 File f = new File(FilePath); if(f.isFile()){ if(f.getName().contains(NameMatch)){ //对匹配的文件将其信息添加到动态数组中 Path.add(FilePath); Name.add(f.getName()); } } if(f.isDirectory()){ File[] array = f.listFiles(); for(int i=0;i<array.length;i++){ showFile(NameMatch,array[i].getPath()); //递归调用,对该文件夹下的每个项目再次执行此函数。 //System.out.println(array[i].getName()); } } }
主函数中命令判断部分代码
boolean[] flag = new boolean[7]; //作为命令判断标志,如果存在命令,则将相应位置位为true for(i = 0;i < 7;i++) flag[i] = false; for(i = 0;i < length;i++){ if(args[i].equals("-c")) flag[0] = true; else if(args[i].equals("-w")) flag[1] = true; else if(args[i].equals("-l")) flag[2] = true; else if(args[i].equals("-a")) flag[3] = true; else if(args[i].equals("-s")) flag[4] = true; else if(args[i].equals("-e")){ flag[5] = true; i++; stopFile = args[i]; //停用词命令后是停用词文件路径 } else if(args[i].equals("-o")){ flag[6] = true; i++; outputFile = args[i]; //输出命令后是输出目的文件 }
四.测试用例设计过程
本次共要求设计10个以上测试用例
我在设计测试用例时首先对每一项功能单独地进行测试,然后组合测试,具体测试用例与测试情况如下:
1.单独对字符统计进行测试
-c input1.txt
2.对输出到文件的功能进行测试
-c input1.txt -o output.txt
3.对单词统计进行测试
-w input2.txt -o output.txt
4.同时对字符和单词统计功能进行测试
-w -c input3.txt -o output.txt
5.对行数统计进行测试
-l input4.txt -o output.txt
6.对文件中空行,代码行,注释行统计功能进行测试
-a input5.txt -o output.txt
7.对文件中除去停用词的单词数进行统计
-w input6.txt -e stopword.txt -o output.txt
8.对文件夹下符合要求的文件统计单词数
-w -s *.txt -o output.txt
9.遍历文件夹下符合要求的文件统计空行 注释行 代码行
-a -s *5.txt -o output.txt
10.遍历文件夹下符合要求的文件,统计多种数据
-a -c -l -s *5.txt -o output.txt
总之,测试用例所秉承的思想是,先进行单元测试,即对不同功能单元进行测试,然后综合测试,检测代码整体是否能够正确运行。
(补充)
测试程序设计思路
在测试代码设计部分,我的思路是调用exe文件执行测试用例,将输出结果记录下来,然后与正确结果进行比对,如果相同,输出测试成功指令,如果不同,则提示错误,如果不能执行而导致无输出,则提示测试用例出现问题。依此法循环执行十个用例,根据最后输出来得出测试结果。
五.参考资料
有关于GitHub的使用参考了廖雪峰的官方网站 :https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/
有关于转化为exe文件参考了博文:https://www.cnblogs.com/YiYeZhiQiu92/p/7277046.html
有关于Java I/O流的使用,参考了博文:https://www.cnblogs.com/tengqiuyu/p/6849097.html
六.个人感想与小结
这次作业我花了很多时间,总结原因还是自己的动手能力太差,以后要多加练习,提高自己的专业能力。
再者,在个人收货方面,通过这次作业,我的Java水平有了一些提高,了解了有关于Java I/O流的操作和对main函数args传参的处理的知识。同时对测试有了初步的接触和了解,测试在软件开发中是一个很重要的环节,自己在这个部分还要多多学习,努力提升自己。