WordCount
github地址:https://github.com/szc1506/WC
一.PSP表格
PSP2.1 |
PSP阶段 |
预估耗时 (分钟) |
实际耗时 (分钟) |
Planning |
计划 |
15 | 25 |
· Estimate |
· 估计这个任务需要多少时间 |
30 | 30 |
Development |
开发 |
280 | 350 |
· Analysis |
· 需求分析 (包括学习新技术) |
100 | 120 |
· Design Spec |
· 生成设计文档 |
50 | 60 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
30 | 20 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
20 | 30 |
· Design |
· 具体设计 |
110 | 80 |
· Coding |
· 具体编码 |
300 | 450 |
· Code Review |
· 代码复审 |
60 | 60 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
200 | 180 |
Reporting |
报告 |
150 | 220 |
· Test Report |
· 测试报告 |
120 | 150 |
· Size Measurement |
· 计算工作量 |
30 | 25 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
60 | 45 |
合计 |
1555 | 1845 |
二.需求说明
WordCount的需求可以概括为:对程序设计语言源文件统计字符数、单词数、行数,统计结果以指定格式输出到默认文件中,以及其他扩展功能,并能够快速地处理多个文件。
wc.exe -c file.c //返回文件 file.c 的字符数
wc.exe -w file.c //返回文件 file.c 的单词总数
wc.exe -l file.c //返回文件 file.c 的总行数
wc.exe -o outputFile.txt //将结果输出到指定文件outputFile.txt
wc.exe -s //递归处理目录下符合条件的文件
wc.exe -a file.c //返回更复杂的数据(代码行 / 空行 / 注释行)
wc.exe -e stopList.txt // 停用词表,统计文件单词总数时,不统计该表中的单
三.程序实现过程
第一次拿到题目时限定语言是java,因为本身对java不是很熟悉,就花了较多的时间在熟悉java和配环境上。参考链接:http://www.runoob.com/java/java-environment-setup.html。题目描述要求统计文件中的字符数,先读入文件,然后字符数,单词数,行数这三个可以一起统计,因为可以一次读一行。扩展功能中的各种代码行,注释行可以用正则式来匹配,递归处理文件夹下文件可以写一个递归函数,停用词表可以先读出所有停用词,放到数组里,然后匹配所有单词,匹配则不用加一。找资料的过程大概就是JAVA的各种使用方法,文末已经给出链接。
四.代码说明
1.字符计数:每读入一个字符,判断是不是回车换行符,不是则字符计数器加一
package java86; import java.io.*; import java.util.*; public class Javatest { public static int countC(String fn) {//统计字符数 BufferedReader reader = null; int res = 0; try { reader = new BufferedReader(new FileReader(fn)); String str = null; while ((str = reader.readLine()) != null) { res += str.length(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } return res; }
2.单词统计:按字符流读取文件,对每一个字符做判断,如果不是换行符或空格则继续往下读取;当读取到换行符或者空格是,将前面读到的字符拼作一个单词,单词计数加一
public static int countW(String fn, List<String> list) {//统计单词数目 BufferedReader reader = null; int res = 0; try { reader = new BufferedReader(new FileReader(fn)); String str = null; while ((str = reader.readLine()) != null) { String[] temp = str.split(" |,"); for (String s : temp) { if (s != null && !list.contains(s)) { ++res; } } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } return res; }
3.行数读取调用java API,统计计数
public static int countL(String fn) {//统计行数 BufferedReader reader = null; int res = 0; try { reader = new BufferedReader(new FileReader(fn)); String str = null; while ((str = reader.readLine()) != null) { ++res; } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } return res; }
4.扩展功能
递归获取文件:获取文件目录,判断是不是目录,如果是目录则递归获取该目录下的内容;如果符合要求的文件,则先将文件名存储,与之后的执行一同进行
代码行,注释行,空行的统计。
对于单词数、字符数、行数的统计:
package text1; import java.io.*; import java.util.ArrayList; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.regex.Pattern; public class extendedFun { //递归处理该目录下的同类型文件 static void allfile(String[] list){ int lenth=list.length; String myfile=null; myfile=list[lenth-1]; if(lenth==8) myfile=list[3]; File directory = new File("");//参数为空 String courseFile; try { courseFile = directory.getCanonicalPath();//获取当前目录路径 // 获得指定文件对象 File file = new File(courseFile); // 获得该文件夹内的所有文件 File[] array = file.listFiles(); File afile=new File(myfile); String fileName=afile.getName(); String fileTyle=fileName.substring(fileName.lastIndexOf("."),fileName.length()); //获取文件类型 for(int i=0;i<array.length;i++) { if(array[i].isFile())//如果是文件 { if(array[i].getName().endsWith(fileTyle)){ if(lenth==2){ if(list[0].equals("-l")||list[0].equals("-c")||list[0].equals("-w")) basecount.count(list[0],array[i].getName()); else if(list[0].equals("-a")){ moredata(array[i].getName(),null); } } else if(lenth==3&&(list[0].equals("-l")||list[0].equals("-c")||list[0].equals("-w"))&&(list[1].equals("-l")||list[1].equals("-c")||list[1].equals("-w"))){ basecount.count(list[0],list[1],array[i].getName()); } else if(lenth==4){ basecount.count(list[0],list[1],list[2],array[i].getName()); } else if(lenth==8) { if(list[0].equals("-a")){ basecount.count(list[1], array[i].getName(),list[7]); basecount.count(list[2], array[i].getName(),list[7]); moredata(array[i].getName(),list[7]); stopcount(array[i].getName(),list[5],list[7]); } } else System.out.println("输入格式错误"); } } } } catch (IOException e) { e.printStackTrace(); } } //统计代码行/空行/注释行 static void moredata(String myfile,String outfile)throws FileNotFoundException { if(outfile==null) outfile="result.txt"; String sfile=new String(myfile); File file=new File(sfile); // 记录注释行数 long annotationLine = 0; // 记录空白行数 long blankLine = 0; // 记录有效代码的行数 long codeLine = 0; //假注释 long notLine=0; if (file == null || !file.exists()) throw new FileNotFoundException(file + ",文件不存在!"); BufferedReader br = null; // 判断此行是否为注释行 boolean comment = false; int whiteLines = 0; int commentLines = 0; int normalLines = 0; try { br = new BufferedReader(new FileReader(file)); String line = ""; 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 { // 正常代码行 //System.out.println(line); normalLines++; } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); br = null; } catch (IOException e) { e.printStackTrace(); } } try{ //打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件 FileWriter writer = new FileWriter(outfile, true); char[] message=(myfile+",代码行/空行/注释行:"+(normalLines+notLine)+"/"+whiteLines+"/"+(commentLines-notLine)+"\r\n").toCharArray();//换行"\r\n"不是"\n" writer.write(message); writer.close(); } catch(IOException e){ System.out.println("File read/write Error"+e); } } } //调用停用词表,重写统计单词数 static void stopcount(String thefile,String txt,String output){ int stopcount=0; int wordcount=0; File stopfile=new File(txt); File file=new File(thefile); ArrayList<String> stop=new ArrayList<String>(3); // 读入stopfile.txt的单词到一个动态string数组中保存 if(stopfile.exists()){ try{ FileInputStream fis=new FileInputStream(stopfile); InputStreamReader isr=new InputStreamReader(fis,"UTF-8"); BufferedReader br=new BufferedReader(isr); 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); } br.close(); isr.close(); fis.close(); } catch(FileNotFoundException e){ e.printStackTrace(); } catch(UnsupportedEncodingException e){ e.printStackTrace(); } catch(IOException e){ e.printStackTrace(); } } //统计stop表的总数目 if(file.exists()){ try{ FileInputStream fis=new FileInputStream(file); InputStreamReader isr=new InputStreamReader(fis,"UTF-8"); BufferedReader br=new BufferedReader(isr); String line=new String(""); StringBuffer sb=new StringBuffer(); 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)); }} } } //System.out.println(wordcount+" "+stopcount+" "+(wordcount-stopcount)); sb.append(line); br.close(); isr.close(); fis.close(); } catch(FileNotFoundException e){ e.printStackTrace(); } catch(UnsupportedEncodingException e){ e.printStackTrace(); } catch(IOException e){ e.printStackTrace(); } } try{ //打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件 if(output==null) output="output.txt"; FileWriter writer = new FileWriter(output, true); char[] message=(thefile+", 单词数(停用后):"+(wordcount-stopcount)+"\r\n").toCharArray();//换行"\r\n"不是"\n" writer.write(message); writer.close(); } catch(IOException e){ e.printStackTrace(); } } }
五.测试过程
测试过程对于每一项功能单独测试,再进行组合测试,确保覆盖了所有可执行的代码,对文件名输入文件和文件夹也作出了测试,总共设计10个测试用例:
(1)wc.exe -l ../test/test.c -o out1.txt (2)wc.exe -w ../test/test.c -o out2.txt
(3)wc.exe -a ../test/test.c -o out3.txt (4)wc.exe -c ../test/test.c -o out4.txt
(5)wc.exe -l -w -a -c ../test/test.c -o out5.txt (6)wc.exe -a ../test/ -o out6.txt
(7)wc.exe -a -s ../test/ -o out7.txt (8)wc.exe -a ../test/*.c -o out8.txt
(9)wc.exe -a ../test/*.c -e ../stop.txt -o out9.txt (10)wc.exe -l -w -a -c -s ../test/ -e ../stop.txt -o out10.tx
六.总结
软件测试工作是一个系统而复杂的工程,软件测试的目的就是确保软件的质量、确认软件以正确的方式做了你所期望的事情,所以工作的主要任务是发现软件的错误、有效定义和实现软件成分由底层到高层的组装过程、验证软件是否满足规格书要求和系统定义文档所规定的技术要求、为软件质量模型的建立提供依据。
而且软件的测试不仅是要确保软件的质量,还要给开发人员提供信息,以方便其为风险评估做相应的准备,以及为其提供分析依据,重要的是要贯穿在整个软件开发的过程中,保证整个 软件开发的过程是高质量的。 软件测试对测试工程师来讲,要求具备较强的专业知识,严谨细心耐心的测试态度,良好的反向思维、发散思维能力、沟通能力等等。
部分代码参考1504班成建伟
六.参考链接
1. http://www.cnblogs.com/winterfells/p/7965596.html
2. http://www.runoob.com/java/java-environment-setup.html
4. http://blog.csdn.net/daxiang_zhang/article/details/2149896
5. http://blog.csdn.net/hpchenqi_16/article/details/48504111
6. http://blog.csdn.net/alex__0805/article/details/50895222
7.https://bbs.csdn.net/topics/390567815