‘’github链接:https://github.com/coolzero2/softwaretest.git
补充说明
关于下面贴代码的时候第6.3条,函数传入的参数是一个动态数组arraylist。实际上,这是因为*在myeclipse中是通配符,在ecl自带的run configurations中输入路径比如“d:/*.txt”,会自动检索当前路径下所有符合条件的文件。但是在CMD中则不会自动检索。如果按照6.3的代码,写出的程序打包成exe后是没办法正确运行的。但是这也算是学习到了一些知识,此处就不再删去了。正确的代码将会在本文的最末贴出。
另外关于指令输入的顺序规定,主要是根据老师给出的白盒测试中的那几个例子来的,这也应用到了judge的逻辑判定中。最终正确顺序:-w,-s,-a,-c,-l,-* 输入文件 -e 停用词表 -o 输出文件
第一次写博客,一方面熟悉一下博客的相关操作,另一方面交一下作业。
本程序最大的缺点应该就是全局变量太多了吧(10+个),这样的程序安全性应该会受到影响;
其次就是代码有些繁琐。
要求提交的时间太紧张,先这样凑合了。以后有时间修改一下。
首先说明各种指令的功能:
-a:统计文件中的代码行、空行、注释行;
-c:统计文件中的字符数,不包括换行符;
-w:统计文件中的单词数;
-l:统计文件的行数;
-e:当使用该指令时,指定停用词表,统计文件中的单词数时不再统计停用词表中的内容。
-s:使用该指令时,指定的输入文件应该是一个路径,统计的信息是路径下所有符合要求的文件的信息;当文件只有一个时,其实看做一个特例(也就是上面所说的单纯统计一个文件)。
-o:使用该指令时,指定输出文件,用来保存统计输入文件后输出的信息。
思考的思路
其实也很简单。这个软件的功能就是用户输入了一组信息,在信息输入的格式正确的情况下,将会输出相关信息。编写这个程序可以分为两点:1.分析用户输入的内容。2.根据分析,执行相应功能。
第2点需要再进行细化。
PSP表格
PSP2.1表格
PSP2.1 |
PSP阶段 |
预估耗时 (分钟) |
实际耗时 (分钟) |
Planning |
计划 |
30 | 10 |
· Estimate |
· 估计这个任务需要多少时间 |
20 | 10 |
Development |
开发 |
480 | 500 |
· Analysis |
· 需求分析 (包括学习新技术) |
40 | 40 |
· Design Spec |
· 生成设计文档 |
60 | 30 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
60 | 30 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
0 | 0 |
· Design |
· 具体设计 |
50 | 30 |
· Coding |
· 具体编码 |
600 | 500 |
· Code Review |
· 代码复审 |
60 | 120 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
120 | 100 |
Reporting |
报告 |
50 | 120 |
· Test Report |
· 测试报告 |
60 | 120 |
· Size Measurement |
· 计算工作量 |
5 | 5 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
5 | 5 |
合计 |
下面上代码。
1.判断用户输入的信息
这里是各种指令对应的flag,逻辑关系依照作业要求设置。为true的时候代表实现相应功能。
private static void judge(String str) { // TODO Auto-generated method stub if(str.equals("-s")&&s_flag==false){ s_flag=true; } else if(str.equals("-*")&&star_flag==false){ star_flag=true; c_flag=true; w_flag=true; l_flag=true; a_flag=true; } else if(str.equals("-a")&&a_flag==false){ a_flag=true; } else if(str.equals("-c")&&c_flag==false){ c_flag=true; } else if(str.equals("-w")&&w_flag==false){ w_flag=true; } else if(str.equals("-l")&&l_flag==false){ l_flag=true; } else if(file_flag==false)//还没有指定输入文件 { if(s_flag==false){//如果输入的不是路径 file_name+=str; txt_name=str; file_flag=true; } else{ pathname=str; path_flag=true; } } else if(str.equals("-e")&&e_flag==false){ e_flag=true; } else if(e_flag==true&&file_flag==true&&o_flag==false&&stop_flag==false){//已经有输入文件且e_flag为真 stoplist+=str; stop_flag=true; } else if(str.equals("-o")&&o_flag==false){ o_flag=true; } else if(o_flag==true&&file_flag==true){//已经有输入文件且o_flag为真 output_name+=str; all_flag=true; } else{ System.out.println("指令错误,请重新输入"); } }
2.统计文件的字符数
换行符不应该统计到字符的总数中,所以此处会去掉。
private static int num_of_char(String filename) { // TODO Auto-generated method stub File file=new File(filename); Reader readfile=null; int c_num=0; try{ readfile = new InputStreamReader(new FileInputStream(file)); int tempchar; while ((tempchar=readfile.read()) != -1) { if((char)tempchar!='\r'&&(char)tempchar!='\n'){ c_num++; } } readfile.close(); } catch(Exception e){ System.out.println("指定输入文件不存在"); } return c_num; }
3.统计单词数
此处需要判断字符是否是大小写字母(IsLetter()),实际上是我自己写的方法,Java中应该有自带的判断字母的方法。
3.1不使用停用词表
private static int num_of_word(String filename){ File file=new File(filename); Reader readfile=null; boolean letter_flag=false; int w_num=0; try{ readfile = new InputStreamReader(new FileInputStream(file)); int tempchar; while ((tempchar=readfile.read()) != -1) { if(IsLetter((char)tempchar)){ letter_flag=true; } else if(letter_flag==true){ letter_flag=false; w_num++; } } readfile.close(); } catch(Exception e){ System.out.println("指定输入文件不存在"); } return w_num; }
3.2使用停用词表
这里就可以看出来我实际上是把上面的一段加了一些内容而已。先凑合用,为了代码的简洁以后还是要修改。
private static int num_of_word_e(String filename) { // TODO Auto-generated method stub File file=new File(filename); File stopfile=new File(stoplist); ArrayList<String>stopstr = new ArrayList<String>(); Reader readfile=null; boolean letter_flag=false; int w_num=0; //读取停用词表内容,保存在stopstr中 try{ readfile = new InputStreamReader(new FileInputStream(stopfile)); int tempchar; String str=""; while ((tempchar=readfile.read()) != -1) { if(IsLetter((char)tempchar)){ letter_flag=true; str+=(char)tempchar; } else if(letter_flag==true){ letter_flag=false; stopstr.add(str); str=""; } } readfile.close(); } catch(Exception e){ e.printStackTrace(); } //判断word数 try{ readfile = new InputStreamReader(new FileInputStream(file)); int tempchar; String str=""; while ((tempchar=readfile.read()) != -1) { if(IsLetter((char)tempchar)){ letter_flag=true; str+=(char)tempchar; } else if(letter_flag==true){ letter_flag=false; w_num++; if(e_flag==true){ for(int i=0;i<stopstr.size();i++){ if(str.compareTo(stopstr.get(i))==0){ w_num--; } } } str=""; } } readfile.close(); } catch(Exception e){ System.out.println("指定输入文件不存在"); } return w_num; }
4.统计行数
遇到换行符就加一。Windows中的换行符为“\r\n”,这里只使用"\n"。
private static int num_of_line(String filename) { // TODO Auto-generated method stub File file=new File(filename); Reader readfile=null; int l_num=1; try{ readfile = new InputStreamReader(new FileInputStream(file)); int tempchar; while ((tempchar=readfile.read()) != -1) { if((char)tempchar=='\n'){ l_num++; } } readfile.close(); } catch(Exception e){ System.out.println("指定输入文件不存在"); } return l_num; }
5.统计文件的代码行/空行/注释行
这里的思路是按行读文件,去除每一行的换行符和空格,获得的string长度为0就代表空行;一行中如果只有“{/*”、“{//”、“//”、“/*”代表注释行(这些就是注释的开头),直到统计到注释的末尾;其余的均为代码行。实际上这里的注释行的表达形式并不清晰、全面,只是与C语言的注释方法类似。比如在/* */的内部又出现//或者/**/就可能会统计错误。
private static int [] code_ana(String filename){ File file=new File(filename); int nothing=0; int line=0; int note=0; int code_line=0; boolean note_flag=false; BufferedReader readfile = null; try{ readfile = new BufferedReader(new FileReader(file)); String tempString = null; while ((tempString = readfile.readLine()) != null) { line++; tempString=tempString.replaceAll("\r\n"," ");//去掉所有换行符和空格 if(note_flag==true){ note++; if(tempString.endsWith("*/")){ note_flag=false;//代表注释内容在本行结束 } } if(tempString.equals(" ")||tempString.equals("{")||tempString.equals("}")){ nothing++; } if(tempString.startsWith("//")||tempString.startsWith("{//")){ note++; } if(tempString.startsWith("/*")||tempString.startsWith("{/*")){ if(tempString.endsWith("*/")){ note++; } else{ note++; note_flag=true;//代表注释的内容在本行还没结束 } } code_line=line-note-nothing; } readfile.close(); } catch(Exception e){ System.out.println("指定输入文件不存在"); } int []num =new int[3]; num[0]=code_line; num[1]=nothing; num[2]=note; return num; }
6.将输出的内容写入特定的文件中
6.1普通的输出
clw这三个功能可以在这里进行相应的输出
private static void output(String output_file, String com,int num) { // TODO Auto-generated method stub File txt=new File(output_file); try{ FileWriter fw=new FileWriter(txt,true);//参数为true代表可以追加写入 BufferedWriter out = new BufferedWriter(fw); out.write(txt_name+","+com+":"+num+"\r\n"); out.close(); } catch(IOException e){ System.out.println("指定输出文件不存在"); } }
6.2统计代码后的输出
仅用于输出统计代码的信息
private static void code_output(String output_file, String com,int []num) { // TODO Auto-generated method stub File txt=new File(output_file); try{ FileWriter fw=new FileWriter(txt,true);//参数为true代表可以追加写入 BufferedWriter out = new BufferedWriter(fw); out.write(txt_name+","+com+":"+num[0]+"/"+num[1]+"/"+num[2]+"\r\n"); out.close(); } catch(IOException e){ System.out.println("指定输出文件不存在"); } }
6.3输入的是路径的时候
此处的work是对第1条中judge后的所有flag进行判断并执行相应功能。第7条对work额外说明。
此处通过*进行分割,前面的是路径,后面的是文件后缀。
private static void files_output(String path_name){ String []path=path_name.split("\\*"); File file = new File(path[0]);//路径boolean exist_flag=false; if(file.exists()){ String []files=file.list(); if(files.length==0){ System.out.println("指定的文件夹是空的"); } else { for(String f:files){ if(f.endsWith(path[1])){ exist_flag=true; txt_name=f; file_name=path[0]+f; work(); } } } if(exist_flag==false){ System.out.println("不存在符合要求的文件"); } } else{ System.out.println("指定路径或文件不存在"); } }
7.主函数
此处的函数中也用到了work()。
实际上work是个void类型的函数,用到的都是全局变量,所以并没有太复杂的结构,以后在改善程序性能的时候,在此函数的基础上会做出完善,减少全局变量等,增加程序的安全性;
另外关于work的功能,只有一个,就是根据flag输出打印最终结果。
所以不再贴出work的代码。
private static void files_output(ArrayList<String> path_name){ for(int i=0;i<path_name.size();i++){ File file=new File(path_name.get(i)); if(file.exists()){ txt_name=file.getName(); file_name=path_name.get(i); work(); } else{ System.out.println("指定路径或文件不存在"); } } }
8.关于全局变量
不得不说确实太多了。另外关于下面代码中自带的路径,其实是我自己测试用,打包成EXE后肯定是要去除的。
static boolean c_flag=false; static boolean w_flag=false; static boolean l_flag=false; static boolean o_flag=false; static boolean a_flag=false; static boolean s_flag=false; static boolean e_flag=false; static boolean file_flag=false; static boolean all_flag=false; static boolean star_flag=false; static boolean path_flag=false; static boolean stop_flag=false; static String txt_name; static ArrayList<String>pathname=new ArrayList<String>(); static String stoplist="D:\\myeclipse8.5\\workspace\\wordcount\\stoplist\\"; static String file_name="D:\\myeclipse8.5\\workspace\\wordcount\\inputfiles\\"; static String output_name="D:\\myeclipse8.5\\workspace\\wordcount\\outputfiles\\";
9.关于测试的设计
第一次像这样比较正式的进行软件的测试。
个人的思路是:程序分为多个函数,每个函数根据指令执行不同的功能可能会产生bug,不同指令之间的组合也有可能会产生bug等,所以测试用例的要点为:
1、必须实验所有的功能
2、实验几组不同功能的组合,看是否能达到预期效果
3、使用不同的测试用例
下面是测试的截图,主要对input的内容进行测试,内容会发生改变(实际上文件内容是以前的各种排序算法)。
9.1.c.o组合
-c input.txt -o output.txt
9.2.w.c.o组合
-w -c input.txt -o output.txt
9.3 l.w.e.o.组合
-l -w input.txt -e stoplist.txt -o output.txt
9.4 *.e.o组合
-* input.txt -e stoplist.txt -o output.txt
9.5 a.o组合
-a input.txt -o output.txt
9.6 s,w,o.组合
此处input的副本是很早之前用的测试文件,所以三个副本的内容相同,但是和现在的input文件内容不想同
-s -w D:/myeclipse8.5/workspace/wordcount/inputfiles/*.txt -o output.txt
9.7 s,l,w,e,o组合
-s -w -l D:/myeclipse8.5/workspace/wordcount/inputfiles/*.txt -e stoplist.txt -o output.txt
9.8 c,l组合
-l -c input.txt
9.9 a,l,w,e,c组合
-l -c -w -a input.txt -e stoplist.txt
9.10 所有指令的组合
-s -l -c -w -a D:\\myeclipse8.5\\workspace\\wordcount\\inputfiles\\*.txt -e stoplist.txt -o output.txt
10、参考文献
无
正确的分析路径(-s指令)的代码:
private static void files_output(String path_name){ String []path=path_name.split("\\*"); File file=new File(path[0]); if(file.exists()){ File[]files=file.listFiles(); if(files!=null){ for(File f:files){ if(f.getName().endsWith(path[1])){ txt_name=f.getName(); file_name=f.getAbsolutePath(); work(); } } } else{ System.out.println("文件夹内容为空"); } } else{ System.out.println(path[0]+":指定路径或文件不存在"); } }