GitHub 項(xiàng)目地址
https://github.com/cosensible/WordCount
PSP表格
PSP2.1 | PSP階段 | 預(yù)估耗時(shí)(分鐘) | 實(shí)際耗時(shí)(分鐘) |
---|---|---|---|
Planning | 計(jì)劃 | 15 | 30 |
Estimate | 估計(jì)這個(gè)任務(wù)需要多少時(shí)間 | 10 | 10 |
Development | 開發(fā) | 30 | 30 |
Analysis | 需求分析(包括學(xué)習(xí)新技術(shù)) | 20 | 50 |
Design Spec | 生成設(shè)計(jì)文檔 | 0 | 0 |
Design Review | 設(shè)計(jì)復(fù)審 | 0 | 0 |
Coding Standard | 代碼規(guī)范(為目前的開發(fā)制定合適的規(guī)范) | 10 | 10 |
Design | 具體設(shè)計(jì) | 30 | 30 |
Coding | 具體編碼 | 600 | 1200 |
Code Review | 代碼復(fù)審 | 30 | 30 |
Test | 測(cè)試 | 120 | 120 |
Reporting | 報(bào)告 | 60 | 90 |
Size Measurement | 計(jì)算工作量 | 5 | 10 |
Postmortem & Process Improvement Plan | 事后總結(jié)花鹅,并提出過程改進(jìn)計(jì)劃 | 20 | 20 |
? | 合計(jì) | 950 | 1630 |
解題思路
拿到題目以后覺得基本功能實(shí)現(xiàn)起來(lái)挺簡(jiǎn)單的智听,擴(kuò)展功能中覺得停用詞表這個(gè)功能挺簡(jiǎn)單二打,只要將文件中的停用詞讀出來(lái)后放到一個(gè)ArrayList中即可,方便以后使用。遞歸讀取文件一開始覺得有些亂禁添,不知如何下手,在網(wǎng)上查了查庙楚,發(fā)現(xiàn)實(shí)現(xiàn)起來(lái)也挺簡(jiǎn)單的上荡,具體參考這里。然而馒闷,對(duì)于 “代碼行/空行/注釋行” 的統(tǒng)計(jì)酪捡,我覺得這里的情況實(shí)在太多了,尤其是對(duì)注釋行的判定,不過,仔細(xì)讀了要求后算是大概知道規(guī)則了引润,不知道自己有沒有理解錯(cuò)济舆。最后狡忙,沒時(shí)間和精力去完成高級(jí)功能了,因?yàn)樽约荷眢w這兩天都快被掏空了。之后就開始編碼了,因?yàn)樽约哼@學(xué)期稍微看了一點(diǎn)Java,所以還算比較熟悉呢袱。但是,對(duì)于測(cè)試的編寫翅敌,自己很迷惑羞福,不知道如何去編寫,想了一會(huì)兒蚯涮,還是決定先把代碼寫完治专,實(shí)現(xiàn)功能再說。
還有一個(gè)要求是要生成exe文件遭顶,這個(gè)第一想法便是Google一下张峰,于是便有了這個(gè)鏈接。
程序設(shè)計(jì)實(shí)現(xiàn)過程
由于功能看起來(lái)還算比較少棒旗,所以自己就沒有詳細(xì)思考如何組織代碼喘批,只是想著每個(gè)功能對(duì)應(yīng)一個(gè)函數(shù),先把基本功能做完再說。做完基本功能后谤祖,覺得代碼量就有些多了婿滓,于是在擴(kuò)展功能開始前老速,自己分析了一下應(yīng)該如何組織這些功能對(duì)應(yīng)的函數(shù)粥喜。基本想法是在主函數(shù)中對(duì)擴(kuò)展功能的參數(shù)進(jìn)行分析橘券,因?yàn)樗麄兓径忌婕暗綄?duì)文件的讀寫操作额湘,然后將基本功能(-c -w -l -a
)封裝在一個(gè)函數(shù)中,方便遞歸分析功能(-s
)的實(shí)現(xiàn)旁舰。由于輸出結(jié)果要按照規(guī)定的順序輸出到文件中锋华,所以在基本功能封裝函數(shù)中用了一個(gè)TreeMap,它對(duì)里面的鍵值對(duì)可以按鍵大小自動(dòng)排序箭窜,所以在讀取信息時(shí)毯焕,可以按照規(guī)定的順序輸出。
代碼說明
返回字符數(shù)
主要使用FileReader
的read()
函數(shù)
while((fileReader.read())!=-1)
++num;
返回文件行數(shù)
主要使用FileReader
的readLine()
函數(shù)
while(fileReader.readLine()!=null)
++num;
返回單詞數(shù)
主要用到String的split()
函數(shù)對(duì)字符分割磺樱。首先跳過空行纳猫,然后將每行中的字符串按空字符(多個(gè)空格、制表符等等)分割后再按逗號(hào)分割竹捉。去掉空字符串后對(duì)單詞進(jìn)行統(tǒng)計(jì)芜辕,統(tǒng)計(jì)時(shí)要用到裝有停用詞的ArrayList。
while ((str = fileReader.readLine()) != null) {
if (!str.equals("")) {//跳過空行
for (String s : str.split("\\s+")) {//將一行以多個(gè)空字符分割
for (String word : s.split(",")) {//以逗號(hào)分割
if (!stopLists.contains(word)&&!word.equals(""))//stopList不含單詞且不為空
num++;
}
}
}
}
返回 “代碼行/空行/注釋行” 數(shù)量
對(duì) “代碼行/空行/注釋行” 的判斷標(biāo)準(zhǔn)如下:
- 代碼行:本行包括多于一個(gè)字符的代碼块差。
- 空行:本行全部是空格或格式控制字符侵续,如果包括代碼,則只有不超過一個(gè)可顯示的字符憨闰,例如
{
状蜗。 - 注釋行:本行不是代碼行,并且本行包括注釋鹉动。一個(gè)有趣的例子是有些程序員會(huì)在單字符后面加注釋:
}//注釋
轧坎,在這種情況下,這一行屬于注釋行训裆。
int[] lineTypes=new int[3];//分別為代碼行眶根,空行,注釋行
while((str=fileReader.readLine())!=null){
str=str.replaceAll("\\s+","");
//行內(nèi)無(wú)字符或者有一個(gè)字符且為'{'或'}'時(shí)边琉,為空行
if (str.equals("")||str.length()==1&&(str.equals("{")||str.equals("}")))
lineTypes[1]++;
else if (str.contains("/*")){
//以/*開頭属百,且位于行首或者前面有一個(gè)字符且為'{'或'}'時(shí),為注釋行
if (str.indexOf("/*")==0||str.indexOf("/*")==1&&(str.charAt(0)=='{'||str.charAt(0)=='}')){
lineTypes[2]++;
if (str.contains("*/"))// 注釋結(jié)束符*/在本行
continue;
while((str=fileReader.readLine())!=null){//注釋結(jié)束符不在同一行
if (str.contains("*/")){
if (str.indexOf("*/")==(str.length()-2)) //注釋結(jié)束符在當(dāng)前行末尾变姨,是注釋行
lineTypes[2]++;
else
lineTypes[0]++;//不在末尾族扰,代碼行
break;
}
lineTypes[2]++;//沒遇到*/結(jié)束注釋前,行都為注釋行
}
}
}
//以//開頭,且位于行首或者前面有一個(gè)字符且為'{'或'}'時(shí)渔呵,為注釋行
else if (str.indexOf("http://")==0||str.indexOf("http://")==1&&(str.charAt(0)=='{'||str.charAt(0)=='}'))
lineTypes[2]++;
else//其他行為代碼行
lineTypes[0]++;
}
遞歸讀取文件
這里用到File
類怒竿,它有兩個(gè)函數(shù):isFile()
和isDirectory()
,分別判斷文件對(duì)象是文件還是目錄扩氢。如果是文件耕驰,判斷它是否符合條件(后綴),符合就加入一個(gè)ArrayList文件集录豺。如果是一個(gè)目錄就進(jìn)行遞歸查找朦肘。
for(int i=0;i<files.length;i++) {
if(files[i].isFile()) {//如果是文件
String tmp=files[i].getName();
//判斷文件名是否符合條件(比如后綴)
if (tmp.indexOf(".c")==tmp.length()-signal.length())//如果包含signal
fileList.add(files[i].getCanonicalPath());//添加文件路徑
}
else if(files[i].isDirectory()) {//如果是文件夾
if (files[i].getName().equals("jre")) {//跳過程序依賴環(huán)境的目錄查找
System.out.println("ignore this catelog named jre.");
continue;
}
//文件夾需要調(diào)用遞歸
fileList.addAll(getFile(files[i].getPath(),signal));
}
}
以上便是主要功能實(shí)現(xiàn)的函數(shù),詳細(xì)代碼可根據(jù)文首給出的項(xiàng)目地址查看双饥。
測(cè)試設(shè)計(jì)
白盒測(cè)試介紹
根據(jù)軟件產(chǎn)品的內(nèi)部工作過程媒抠,在計(jì)算機(jī)上進(jìn)行測(cè)試,以證實(shí)每種內(nèi)部操作是否符合設(shè)計(jì)規(guī)格要求咏花,所有內(nèi)部成分是否已經(jīng)過檢查趴生。這種測(cè)試方法就是白盒測(cè)試。白盒測(cè)試把測(cè)試對(duì)象看做一個(gè)打開的盒子昏翰,允許測(cè)試人員利用程序內(nèi)部的邏輯結(jié)構(gòu)及有關(guān)信息苍匆,設(shè)計(jì)或選擇測(cè)試用例,對(duì)程序所有邏輯路徑進(jìn)行測(cè)試矩父。通過在不同點(diǎn)檢查程序的狀態(tài)锉桑,確定實(shí)際的狀態(tài)是否與預(yù)期的狀態(tài)一致。這里主要采用基于獨(dú)立路徑的測(cè)試方法窍株。
統(tǒng)計(jì)字符數(shù)或行數(shù)
統(tǒng)計(jì)字符數(shù)和行數(shù)類似民轴,加入了停用單詞表的參數(shù)后,只需要最后一個(gè)分支節(jié)點(diǎn)加一個(gè)判定條件即可球订,對(duì)以下程序圖無(wú)影響后裸。該程序圖有一個(gè)分支節(jié)點(diǎn),所以有兩條獨(dú)立路徑冒滩。
路徑 | 輸入 | 預(yù)期輸出 | 實(shí)際輸出 |
---|---|---|---|
A->B->C | NULL | 0 | 0 |
A->B->B->C | 'a' | 1 | 1 |
統(tǒng)計(jì)單詞數(shù)
共有五個(gè)分支節(jié)點(diǎn)微驶,故有六條獨(dú)立路徑。
路徑 | 輸入 | 預(yù)期輸出 | 實(shí)際輸出 |
---|---|---|---|
A->B->C->D->E->F->G | 空字符 + ,test
|
1 | 1 |
A->B->G | NULL | 0 | 0 |
A->B->C->B->C->D->E->F->G | 空行 + 空字符 + ,test
|
1 | 1 |
A->B->C->D->B->C->D->E->F->G | 不存在 | ||
A->B->C->D->E->D->E->F->G | 不存在 | ||
A->B->C->D->E->F->D->B->G | 空字符行 | 0 | 0 |
統(tǒng)計(jì) “代碼行/空行/注釋行”
由于代碼行/空行/注釋行的判定標(biāo)準(zhǔn)很復(fù)雜开睡,所碰到的情況也很多因苹,會(huì)產(chǎn)生很多判定節(jié)點(diǎn),其程序圖如下圖所示:
這個(gè)程序圖非常復(fù)雜篇恒,我盡量畫得很簡(jiǎn)潔了扶檐,但是看起來(lái)依舊不好看⌒布瑁可以看到款筑,圖中有11個(gè)判定節(jié)點(diǎn)智蝠,所以共有12條獨(dú)立路徑,具體的測(cè)試過程非常復(fù)雜奈梳,以下直接給出測(cè)試的文本文件:
test()
{
File writename = new File(outputPath);
writename.createNewFile();
codeLine*/
BufferedWriter out = new BufferedWriter(new FileWriter(writename));
//noteLine
out.write(outputBuffer);
/*noteLine
/*noteLine
*/
/*noteLine*/
/*noteLine
//noteLine
*/codeLine
out.flush();
out.close();
}//noteLine
for(){
}/*noteLine*/
預(yù)期輸出:atest.c
杈湾,代碼行/空行/注釋行: 10/3/9
實(shí)際輸出:atest.c
,代碼行/空行/注釋行: 10/3/9
返回統(tǒng)計(jì)結(jié)果
這個(gè)函數(shù)組裝幾個(gè)基本功能(-c -w -l -a
參數(shù))攘须,主要用來(lái)給遞歸統(tǒng)計(jì)信息功能服務(wù)漆撞。以下是基本程序圖:
通過分析知道有5條獨(dú)立路徑。
路徑 | 輸入 | 預(yù)期輸出 | 實(shí)際輸出 |
---|---|---|---|
A->B->C->G->H | 輸入命令-c
|
輸出字符統(tǒng)計(jì)結(jié)果 | 輸出字符統(tǒng)計(jì)結(jié)果 |
A->B->D->G->H | 輸入命令-w
|
輸出單詞統(tǒng)計(jì)結(jié)果 | 輸出單詞統(tǒng)計(jì)結(jié)果 |
A->B->E->G->H | 輸入命令-l
|
輸出行數(shù)統(tǒng)計(jì)結(jié)果 | 輸出行數(shù)統(tǒng)計(jì)結(jié)果 |
A->B->F->G->H | 輸入命令-a
|
輸出行類型統(tǒng)計(jì)結(jié)果 | 輸出行類型統(tǒng)計(jì)結(jié)果 |
A->B->C->G->B->D->G->H | 輸入命令-c -w
|
輸出字符和單詞統(tǒng)計(jì)結(jié)果 | 輸出字符和單詞統(tǒng)計(jì)結(jié)果 |
遞歸獲取滿足條件的文件
該函數(shù)遞歸獲取當(dāng)前目錄下的所有給定后綴的文件路徑阻课,且返回文件路徑結(jié)果集叫挟,這里的分析以.c
后綴為例艰匙。程序圖如下圖所示:
只有五條獨(dú)立路徑是有效的限煞,如下表所示:
路徑 | 輸入 | 預(yù)期輸出 | 實(shí)際輸出 |
---|---|---|---|
A->B->G | 目錄下無(wú)文件且無(wú)目錄 | 空文件集 | 空文件集 |
A->B->C->E->B->G | 目錄下只有一個(gè)文件且后綴為.c
|
返回一條結(jié)果 | 返回一條結(jié)果 |
A->B->C->B->G | 目錄下只有一個(gè)文件且后綴不是.c
|
空文件集 | 空文件集 |
A->B->D->F->B->G | 目錄下只有一個(gè)jre 目錄 |
空文件集 | 空文件集 |
A->B->D->B->C->E->B->G | 目錄下有一個(gè)只含有一個(gè).c 文件的不為jre 的目錄 |
一條結(jié)果 | 一條結(jié)果 |
以上內(nèi)容就是主要功能的測(cè)試設(shè)計(jì)。
WordCount 使用說明
具體使用說明詳見項(xiàng)目地址员凝。