1、Fork倉庫的Githup位址:https://github.com/371091997/PersonProject-Java
2、PSP表格記錄估計将在程式的各個子產品的開發上耗費的時間
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
Planning | 計劃 | 45 | 10 |
• Estimate | • 估計這個任務需要多少時間 | 30 | 15 |
Development | 開發 | 300 | 100 |
• Analysis | • 需求分析 (包括學習新技術) | 480 | 400 |
• Design Spec | • 生成設計文檔 | 20 | |
• Design Review | • 設計複審 | 10 | 3 |
• Coding Standard | • 代碼規範 (為目前的開發制定合适的規範) | 5 | |
• Design | • 具體設計 | 20 | |
• Coding | • 具體編碼 | 540 | 600 |
• Code Review | • 代碼複審 | 60 | |
• Test | • 測試(自我測試,修改代碼,送出修改) | 120 | |
Reporting | 報告 | ||
• Test Repor | • 測試報告 | 60 | |
• Size Measurement | • 計算工作量 | 30 | |
• Postmortem & Process Improvement Plan | • 事後總結, 并提出過程改進計劃 | ||
合計 | 1885 | 1273 |
3.解題思路:拿到題目之後,大概直到題目對我們的幾個要求,首先是先要實作幾個功能,一個功能是讀取檔案,然後再實作計算字元數,計算行數,計算單詞數,計算前十個出現頻率的單詞,再實作這些之後再将代碼封裝;
首先,我看java書的時候看到過讀取檔案内容的知識,我翻書并且按照書中的内容打出了讀取檔案的代碼,然後要實作幾個功能,這些功能再曾經學C++的時候是使用一些自己寫的算法來實作的,但是我知道Java提供了很多的接口和類方法,是以通過網絡和書籍,找到幾個常用類并且加上自己寫的一些算法實作了這些方法;
讀取檔案内容不再贅述,就是通過File和FileInputStream類實作的;然後是計算字元數,這個同樣的很簡單,隻要通過FileReader和BufferedReader按行讀取檔案之中的内容,然後判斷所讀取出的字元串中的字元的ASCII碼就能計算字元數了;
計算行數的話,同樣的使用FileReader和BufferedReader按行讀取,自然可以計算行數;
計算單詞數,就是使用正則式,然後通過正則式分割字元串,然後判斷是否是需要的單詞就可以了;
最後一個要計算單詞出現次數并且按照頻率和字典序進行排序,這個我感覺有點兒困難,一開始我是想使用三個數組來實作,其中兩個字元串數組,一個計算出現頻率的數組,通過對頻率的計算,然後将整型數組按照其值從大到小進行排序,并且同時,整型數組對應的字元串數組同樣的進行排序,然後将相同頻率的字元串數組中的元素加入到另一個字元串中,将另一個字元串按照字典序進行排序,再将排序完的字元串數組傳回原本的字元串,這個工作一樣就 會很大,是以雖然有思路,但是我不想這麼做,通過查詢課本和網絡,使用了HashMap和List類來代替了上述的數組的功能,減少了操作,通過HashMap統計其中的單詞機器頻率,然後轉換為List類,調用sort方法,重寫compare方法,實作按照value從大到小的排序,然後就通過字元串數組将相同頻率的字元串給進行字典排序,輸出到最終的字元串和整型數組之中最後就是實作封裝了,這個沒什麼好說的,就是将實作幾個功能的函數方法放到不同的class之中,然後使用統一的接口,需要的時候調用接口,然後重寫接口方法,就實作了封裝。
4.計算子產品接口的設計與實作過程
①代碼的設計過程
實作功能:統計檔案中的字元數;
統計檔案中的行數;
統計檔案中的單詞數;
統計檔案中的出現頻率最高的10個單詞數并且将相同頻率的單詞按照單詞的字典序排序;
封裝要求:統計字元數
統計單詞數
統計最多的10個單詞及其詞頻
這三個功能獨立出來,成為一個獨立的子產品(class library, DLL, 或其它),稱之為計算核心"Core子產品",這個子產品至少可以在幾個地方使用:指令行測試程式使用;在單元測試架構下使用;與資料可視化部分結合使用,并且設計API接口,然後使用Core封裝接口
代碼組織:
實作功能 | 實作方式 | 函數名/類名 |
統計字元 | 類實作 | charactersnumber |
統計行數 | 函數實作 | lines |
統計單詞 | wordsnumber | |
統計10個單詞及其頻率 | flequentwords |
算法關鍵:
算法關鍵 | |
通過從統計檔案中所讀出來的每一行字元串中符合ASCII碼表中的字元,加到count之中,進而統計字元數 | |
通過 | |
通過設定正規表達式來分隔字元串之中的單詞,并且設定判定條件判斷是否是符合條件的單詞,進而統計單詞數 | |
先統計單詞,通過HashMap來計算各個單詞出現次數,然後将HashMap轉換為List,再通過Entry的getvalue方法得到value值周遊前十個單詞,若是value值相同的放在一起用 |
②單元測試
測試方法:我是用eclipse自帶的junit4進行測試,使用了四組比較複雜的資料,測試均成功,測試資料如下,這是其中一組資料,四組資料均此規模

Junit4的測試結果:
上面資料的運作結果:
junit4測試代碼(其一,共四組)
代碼覆寫率測試(我是用java自帶的Eclemma,我感覺覆寫率還闊以):
5.計算子產品接口部分的功能改進:
計算子產品wordsnumber思路及其改進:
思路一:将檔案中的字元轉換為字元串,然後判斷其是否前四個是字母,若是則單詞計數加一,若是遇到非數字并且非字母的字元,則跳過;若是遇到連續四個字母,則繼續判斷直到遇到非字母和數字的字元,開始重新判斷字母
思路二(改進):由于一個個的判斷字元太過麻煩,通過正則式将字元串分隔開來,然後判斷每一個的字元串數組之中的元素是否前四個字元是字母,若是,則單詞計數器加一
計算子產品flequentwords思路及其改進:
思路一:通過判斷是否是單詞,然後将單詞加入字元串數組,并且在加入之前判斷該單詞是否在字元串數組之中,若是,則同該單詞在字元串數組中的位置i,在整數數組中的位置i上加一,直到判斷完,然後将整型數組排序,在排序的過程中同時的對字元串數組進行相同的排序操作,于是就得到了按照出現頻率排序的字元串數組,然後通過判斷相同的出現頻率的字元串數組并且将其放到另一個字元串數組中按照字典順序進行排序,然後将排序好的數組傳回整體的字元串數組之中,就得到了按照字典序和出現頻率排序的字元串數組,就能夠輸出得到需要的答案
思路二:由于需要對兩個數組進行操作,然後再對一個字元串數組進行操作,是以操作複雜,這時候引入了HashMap,在判斷完是單詞之後将其放入HashMap之中,若是已經存在于HashMap,則将其value值加一,若是不在則将key和value值加入HashMap之中,然後再HashMap轉為List将其按照value值進行排序,然後就輸出到數組之中,将其出現頻率相同的字元串按照字典序進行排序,就得到了所需要的答案
6.代碼說明
1 //接口代碼
2 public interface Core {
3 void Mycount(File file1,File file2);
4 }
//統計單詞頻率并且排序
class flequentwords implements Core {
//重寫排序方法
private static class ValueComparator implements Comparator<Map.Entry<String,Integer>>
{
public int compare(Map.Entry<String,Integer> m,Map.Entry<String,Integer> n)
{
return n.getValue()-m.getValue();
}
}
private File file1;
private File file2;
public void Mycount(File file3,File file4) {
file1=file3;
file2=file4;
try {
Map<String,Integer> map=new HashMap<>();
//讀取檔案内容
InputStream input=null;
input=new FileInputStream(file1);
byte b[]=new byte[(int)file1.length()];
for(int i=0;i<b.length;i++)
{
b[i]=(byte)input.read();
}
input.close();
String str=new String(b);
//分割讀取的字元串
String regex="[\\s\\p{Punct}]+";
String words[]=str.split(regex);
//判斷分割的字元串是否是單詞
for(int i=0;i<words.length;i++)
{
if(words[i].length()<4)
continue;
boolean judge=true;
for(int j=0;j<=3;j++)
{
if(words[i].charAt(j)<'A'||words[i].charAt(j)>'z')
judge=false;
}
//如果是單詞,就判斷是否已經再HashMap之中
if(judge)
{
if(map.containsKey(words[i]))//如果再HashMap之中,則将其頻率,也就是value加1
{
map.put(words[i], map.get(words[i])+1);
}
else//若是不再HashMap之中,則加入到其中,并且value為1
{
map.put(words[i], 1);
}
}
}
//将HashMap轉換為List
List<Map.Entry<String,Integer>> list=new ArrayList<>();
list.addAll(map.entrySet());
//将list按照value值進行排序
ValueComparator vc=new ValueComparator();
Collections.sort(list,vc);
Entry<String, Integer> te=list.get(0);
int count=te.getValue();
String[] change=new String[list.size()];
int[] intchange=new int[list.size()];
//将排序之後的list放入數組,一個是字元串數組,一個是整型數組
for(int i=0;i<list.size();i++)
{
te=list.get(i);
change[i]=te.getKey().toLowerCase();
intchange[i]=te.getValue();
}
//将頻率相同的字元串進行字典排序
for(int i=1,jet=0;i<list.size()&&i<10;i++)
{
te=list.get(i);
if(count!=te.getValue()||i==9||i==list.size()-1)
{
if(i==9||i==list.size()-1) i++;
String[] change2=new String[i-jet];
for(int j=jet,k=0;j<i;j++,k++)
{
change2[k]=change[j];
}
Arrays.sort(change2);
for(int j=jet,k=0;j<i;j++,k++)
{
change[j]=change2[k];
}
jet=i;
}
count=te.getValue();
}
FileWriter out=new FileWriter(file2,true);
for(int i=0;i<list.size()&&i<10;i++)
{
String result="<"+change[i]+">: "+String.valueOf(intchange[i])+"\r\n";
if(i==list.size()-1||i==9)
result="<"+change[i]+">: "+String.valueOf(intchange[i]);
out.write(result);
}
out.close();
}catch(IOException e) {
System.out.println(e);
}
}
}
//單詞數統計
public class wordsnumber implements Core {
private int count=0;
private File file1;
private File file2;
public void Mycount(File file3,File file4) {
file1=file3;
file2=file4;
try {
//讀取檔案内容
InputStream input=null;
input=new FileInputStream(file1);
byte b[]=new byte[(int)file1.length()];
for(int i=0;i<b.length;i++)
{
b[i]=(byte)input.read();
}
input.close();
//使用正則式對讀取的字元串進行分割
String regex="[\\s\\p{Punct}]+";
String str=new String(b);
String words[]=str.split(regex);
//判斷是否是符合标準的單詞,若是則加1
for(int i=0;i<words.length;i++)
{
if(words[i].length()<4)
continue;
boolean judge=true;
for(int j=0;j<=3;j++)
{
if(words[i].charAt(j)<'A'||words[i].charAt(j)>'z')
judge=false;
}
if(judge)
{
count++;
}
}
String wordstring="words: "+String.valueOf(count)+"\r\n";
FileWriter out=new FileWriter(file2,true);
out.write(wordstring);
out.close();
}catch(IOException e) {
System.out.println(e);
}
}
}
//字元數統計
public class charactersnumber implements Core{
private int count=0;
private File file1;
private File file2;
public void Mycount(File file3,File file4)
{
file1=file3;
file2=file4;
try
{
//讀取檔案中的内容
FileReader inone=new FileReader(file1);
BufferedReader buf=new BufferedReader(inone);
String str=null;
//按行讀取
while((str=buf.readLine())!=null)
{
count++;
for(int i=0;i<str.length();i++)
{
if((int)str.charAt(i)>=0&&(int)str.charAt(i)<128)//若是ASICC表中的字元,則加1
{
count++;
}
}
}
count--;
buf.close();
String characters="characters: "+String.valueOf(count)+"\r\n";
FileWriter out=new FileWriter(file2,true);
out.write(characters);
out.close();
}
catch(IOException e)
{
System.out.println(e);
}
}
}
7.心路曆程和收獲
說真的,我之前都是寫的都是算法之類的東西,沒接觸過封裝,接口什麼的,也是第一次使用Java打代碼,是以耗時頗大,但是我相信會越來越好;我之前不知道項目開發什麼樣子的,雖然這個算不上什麼項目開發,但是也讓我多多的了解了之前的一些比如封裝什麼的不知道的東西,然後我憑借自己的想象,大概想象出了那些項目開發人怎麼開發項目的,然後我的迷茫就少了一些,軟體工程實踐雖然有點累,然是吧我覺得是挺好的,希望自己能夠再接再厲;我之前聽說軟體工程實踐不好做,我也做好了心理準備了,是以這個算是再我的意料之中吧,就是寫部落格是真的累,這個部落格園沒有CSDN的好用,很多功能沒有,難受;我感覺自己對程式語言的學習能力又增強了一步,這個很好,單元測試的話,嗯,我其實試過了,并且按照慕課網上的課程一步一步的做,用的是junit4