‘’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]+":指定路徑或文件不存在"); } }