Github项目地址: https://github.com/jianjake/word-
1、PSP表格
PSP2.1表格
PSP2.1 |
PSP阶段 |
预估耗时 (分钟) |
实际耗时 (分钟) |
Planning |
计划 |
||
· Estimate |
· 估计这个任务需要多少时间 |
20 | 10 |
Development |
开发 |
||
· Analysis |
· 需求分析 (包括学习新技术) |
20 | 10 |
· Design Spec |
· 生成设计文档 |
20 | 10 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
120 | 130 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
120 | 130 |
· Design |
· 具体设计 |
30 | 20 |
· Coding |
· 具体编码 |
240 | 250 |
· Code Review |
· 代码复审 |
30 | 30 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
30 | 30 |
Reporting |
报告 |
||
· Test Report |
· 测试报告 |
20 | 30 |
· Size Measurement |
· 计算工作量 |
30 | 30 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
30 | 30 |
合计 |
710 | 680 |
2、解题思路
1、拿到题后,一看输入格式wc.exe -c file.c就蒙了,后来查了一下百度,才知道原来是在主动类中main的String[] args中的参数,在以往都不用这个参数,经常忽略它的存在和它的意义。
2、动手进行Java编程时候,发现Java生成的不是exe文件,而现在要生成exe文件,这时候在网上找了一下,在【1】有详细的指导方法。但是后来,我在同学的电脑上运行自己导出的exe文件,发现NO JVM could be found on your system异常,网上【2】说我的32位系统导出的应用在64位上有冲突。
3、在做使用停用词表统计单词的时候思路,在网上查了一下,发现是运用正则表达式匹配单词 来计算单词的个数,【3】后来,在测试中发现多个空格出现的错误情况,需要进行去除多空格【4】
3、程序设计实现过程。
类:三个类,即wc类、basecount类和extendedFun类函数:主函数main(String[] args)、基本功能的字符数单词数和行数统计函数print(Sting action1,String source)、写函数putAtoB(String message,String thefile)
处理同目录的文件函数allfile(String[] list)、统计代码行/空行/注释行函数moredata(String myfile)以及调用停用词表重新计算单词数函数stopcount(String thefile,String txt)。
之间的关系:主函数根据参数的个数类型,进行调用其他函数,几乎所有需要进行写文件操作的函数都要调用写函数putAtoB,将信息写进指定的(或默认的)文件中。
关键函数绘制流程图:
其中一个模块,例如
其他的就不一一列出。
4、关键代码:
(1)字符数、单词数和行数
while((line=br.readLine())!=null) { linecount++; sb.append(line); charcount+=line.length(); String[] split = line.split("\\s++|\\.|,|\\;|\\(|\\)|\\[|\\]|\\<|\\>|\\=|\\-|\\+|\\*|\\/|\\{|\\}"); //设置单词划分的要求 for (int i = 0; i < split.length; i++) { // 获取到每一个单词 Integer integer = map.get(split[i]); // 如果这个单词在map中没有,赋值1 if(null==integer){ map.put(split[i], 1); }else{ // 如果有,在原来的个数上加上一 map.put(split[i], ++integer); } } } // 遍历,根据key获取所对应的value Set<String> keySet = map.keySet(); for (String string : keySet) if(!(string.equals("")))//测试时候发现,去除不了多个空格的要求 wordcount+=map.get(string);
单词的划分:由空格或逗号分割开的都视为单词,且不做单词的有效性校验,例如:thi#,that视为用逗号隔开的2个单词。
(2)扩展功能
//统计代码行/空行/注释行 while ((line = br.readLine()) != null) { line = line.trim(); if (line.matches("^[//s&&[^//n]]*$")||line.equals("{")||line.equals("}")) { /* 空行 :本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”*/ whiteLines++; } /* 本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释: * }//注释 */ else if (line.startsWith("/*") && !line.endsWith("*/")|| ((line.startsWith("{/*")||line.startsWith("}/*"))&&!line.endsWith("*/"))){ // 判断此行为"/*"开头的注释行 commentLines++; comment = true; } else if (comment == true && !line.endsWith("*/") &&!line.startsWith("*/")) { // 为多行注释中的一行(不是开头和结尾) notLine++;//虚假的注释行计数 commentLines++; } else if (comment == true && (line.endsWith("*/")||line.startsWith("*/"))) { // 为多行注释的结束行 commentLines++; comment = false; } else if (line.startsWith("//")|| line.startsWith("}//")||line.startsWith("{//")|| ((line.startsWith("{/*") ||line.startsWith("}/*")||line.startsWith("/*")) && line.endsWith("*/"))) { // 单行注释行 commentLines++; } else { // 正常代码行 normalLines++; }
解释思路与注释说明:依据读取行内容来进行判断行的类型。line.matches进行检验匹配情况,但是空行的情况又加了两种情况,即只有“{”或 “}”而没有其他显示的字符,故判断情况需加上这两种情况的判断方式。统计注释行的时候需要注意前面有”/*”但后面没有”*/”配对,这是虚假的注释,故在多行(/*……….*/)注释的时候,要进行相关的记录,用于修正没有”*/”的情况,还有就是注意这两种注释情况,即在一行中首字符是”{“或“}”然后紧跟”/*”的注释情况。
//统计使用stop表后的的单词总数目 //调用停用词表,重写统计单词数 ArrayList<String> stop=new ArrayList<String>(3); // 读入stopfile.txt的单词并放进入到一个动态string数组中保存,以便于后面遍历 String line=new String(""); StringBuffer sb=new StringBuffer(); TreeMap<String, Integer> map = new TreeMap<>(); String[] split =null; while((line=br.readLine())!=null){ sb.append(line); split = line.split("\\s+"); for (int i = 0; i < split.length; i++) { // 获取到每一个单词 Integer integer = map.get(split[i]); // 如果这个单词在map中没有,赋值1 if(null==integer){ map.put(split[i], 1); } } } Set<String> keySet = map.keySet(); for (String string : keySet) { stop.add(string); } //遍历要统计的文件,进行统计各单词的总数 TreeMap<String, Integer> map = new TreeMap<>(); while((line=br.readLine())!=null){ String[] split = line.split("\\s++|\\.|,|\\;|\\(|\\)|\\[|\\]|\\<|\\>|\\=|\\-|\\+|\\*|\\/|\\{|\\}|\\_"); //去除多个空格\\s+ for (int i = 0; i < split.length; i++) { // 获取到每一个单词 Integer integer = map.get(split[i]); // 如果这个单词在map中没有,赋值1 if(null==integer){ map.put(split[i], 1); }else{ // 如果有,在原来的个数上加上一 map.put(split[i], ++integer); } } } // 遍历,根据key获取所对应的value ,累计单词的总数, //同时统计停用词表在该文件中的总数 Set<String> keySet = map.keySet(); for (String string : keySet) { int i=0; if(!(string.equals(""))){//去掉空格符 wordcount+=map.get(string);//统计单词总数 while(i<stop.size()){//遍历”停用词表” if(string.equalsIgnoreCase(stop.get(i++)))//不区分大小写判断 { stopcount+=map.get(string);//统计停用词在该文件中的总数 //System.out.println(string+":"+map.get(string)); }} }
解释思路与注释说明:先将“停用词”从停用词表读入,并放在变长的数组中,以便后面的遍历统计;其次是统计目标文件的总单词数,同时遍历停用词数组进行统计停用词在目标文件的总数目;最后将总单词数减去停用词在该文件的总数,即可得到结果。
5、测试设计过程
如何设计测试用例:为了检测是否满足需求分析所得的要求功能与结果正确性,我就从参数设计与读取测试文件,进行测试。一方面参数的设置是为了满足用户的多方式输入,测试文件是针对结果的正确性进行验证与测试。
①测试 -c chartest.c,文件chartest.c
②测试 -l filen.c -o out.txt文件filen.c
③测试 -w file2.c -e stoplist.txt 文件file2.c
④测试 -l -w -c sort.c文件sort.c
⑤测试 – a file1.c -o out.txt,文件file1.c
⑥测试-a -l add.c文件add.c
⑦测试 -w file.c -e stoplist.txt -o out.txt,文件stoptest.c
⑧测试-s -a -w -l file.c -o out.txt ,文件file.c
⑨测试-s -l -w -c file.c -o out.txt,文件 adom.c
⑩测试-s -a -l -w file.c -e stop.txt -o out.txt文件success
程序高风险:
首先是参数的多样与复杂,这会派生出很多判断与分支,特别是条件一多就容易出现书写错误和判断错误等。其次是,边缘地方也会导致程序的高风险,比如在统计行数的时候,若果文件末尾有很多空,在读取文件最后一行的时候,会导致出现,程序误认为读到文件结束,而返回一个null的值,这时导致最后一行未能统计。再者,程序的高风险还在一些程序员默认的地方,因为见多识广后就会在潜意识里默认用户会遵守这些“潜规则”,然后就忽视了一些必要的判断,从而导致出现错误。最后,程序的高风险还在于一些高风险的操作,比如一些读写操作等。
测试代码设计:从简单的开始,一步步的推广,在推广的过程中,最好是将调用函数方式类似的进行测试,以减少因为调用函数方式相似而认为已经测试过,尽可能都测一遍。在保证能测试到大量的节点后,可以选择随机的测试,保证程序不会因为随机性而出现一些错误。
6、参考文献链接
【1】http://blog.csdn.net/sunkun2013/article/details/13167099
【2】http://blog.csdn.net/Landlord921/article/details/37600721
【3】http://blog.csdn.net/fuxuemingzhu/article/details/41894871
【4】http://blog.csdn.net/liruizhuang/article/details/5807576
来源:https://www.cnblogs.com/jakejian/p/8613002.html