天天看點

第1次作業

要求0:作業要求位址:https://edu.cnblogs.com/campus/nenu/2016CS/homework/2110

要求1:GIT倉庫位址:https://git.coding.net/wudb527/wf.git (master2為中間的過程,V1.0為最終送出的版本,随後對V1.0和主分支master進行了合并)

要求2:PSP階段表格

SP2.1 預估時間600(min) 實際開發時間1043(min)
計劃 20 40
·明确需求和其他相關因素,估計每段時間成本
開發 490 903
·需求分析 60
·生成設計文檔 30
·設計複審(和同學稽核設計文檔)
·代碼規範(為目前的開發制定合适的規範) 10
·具體設計 50
·具體編碼 300 673
·代碼複審
·測試(自測、修改代碼、送出修改)
報告 90 100
·測試報告 35
·計算工作量
·事後總結,并提出過程改進計劃
功能子產品 具體階段 預計時間(min) 實際時間(min)
功能1 具體設計 15
具體編碼 130
測試完善 12
功能2
70 246
13
功能三
180 290

分析差距原因:

(1):沒有完全按照軟體開發的流程來,比如因為感覺是小程式,設計文檔就沒有做,雖然這一項我沒有花時間,但是實際中我卻在其他地方饒了彎路。

(2):我對C++的文法沒有足夠的熟悉,有些資料類型的内置函數不夠了解(比如string.c_str()),導緻在應用的時候還要上網查詢。

(3):在之前沒有對題目要求做足夠的了解,導緻,一邊寫代碼還要傳回去看題目要求,花費了不少時間。

要求3:

1.解題思路描述

我覺得這個程式的流程大概就是讀取檔案獲得字元串,然後對字元串進行操作獲得符合題目要要求的字元,而後進行字元串計數,然後按照題目要求輸出就可以了。當第一眼看到這個題目的時候我覺得難點在打開檔案、路徑輸入儲存以及進行檔案夾下的檔案查詢,而重點點我覺得有正規表達式的應用。我一開始就打算用C++裡STL裡面的map容器來實作字元串的計數。但是檔案的打開以及檔案夾下面的檔案讀取卡了不少時間,本來打算用C的fopen實作,但是fopen打開檔案後獲得是字元數組,我打算用string類型儲存的單詞的,是以還要把字元數組轉為字元串,有點繁瑣,後來我查找資料知道了C++的檔案流,這是一大突破,而在檔案夾内部的檔案周遊上我查資料知道了 _findfirst()函數可以實作問價的查詢。

2.代碼解釋

難點1:檔案的打開

用檔案流的話,我們使用流提取運算符( >> )從檔案讀取資訊,就像使用該運算符從鍵盤輸入資訊一樣。唯一不同的是,在這裡您使用的是 ifstream 或 fstream 對象,而不是 cin 對象。

下面是檔案流的使用方法,用于對某一個檔案進行讀取然後輸出。

1 #include<iostream>
 2 #include<fstream>
 3 using namespace std;
 4 int main()
 5 {
 6     // 以讀模式打開檔案
 7     ifstream infile;
 8     string path = "D:\\codeblocks\\wf\\input.txt";
 9     //打開檔案
10     infile.open(path.c_str());
11     //括号裡應該是字元串類型,是以要用到string.c_str()進行轉換
12     string str;
13     while(infile>>str)
14     {
15         cout<<str<<endl;
16     }
17     return 0;
18 }      

難點2:路徑内檔案夾的周遊,檔案的查找

_findfirst是按照檔案名的字典序查找的,是以題目要求的檔案名排序問題直接解決了,隻要能找到檔案那就是符合要求的。

這兩個函數均在io.h裡面。

首先了解一下一個檔案結構體:

struct _finddata_t {

    unsigned    attrib;

    time_t      time_create;   

    time_t      time_access;   

    time_t      time_write;

    _fsize_t    size;

    char        name[260];

}; 

time_t,其實就是long

而_fsize_t,就是unsigned long

現在來解釋一下結構體的資料成員吧。

attrib,就是所查找檔案的屬性:_A_ARCH(存檔)、_A_HIDDEN(隐藏)、_A_NORMAL(正常)、_A_RDONLY(隻讀)、_A_SUBDIR(檔案夾)、_A_SYSTEM(系統)。

time_create、time_access和time_write分别是建立檔案的時間、最後一次通路檔案的時間和檔案最後被修改的時間。

size:檔案大小

name:檔案名。

再來看一下_findfirst函數:long _findfirst(const char *, struct _finddata_t *);

第一個參數為檔案名,可以用"*.*"來查找所有檔案,也可以用"*.cpp"來查找.cpp檔案。第二個參數是_finddata_t結構體指針。若查找成功,傳回檔案句柄,若失敗,傳回-1。

然後,_findnext函數:int _findnext(long, struct _finddata_t *);

第一個參數為檔案句柄,第二個參數同樣為_finddata_t結構體指針。若查找成功,傳回0,失敗傳回-1。

最後:_findclose()函數:int _findclose(long);

隻有一個參數,檔案句柄。若關閉成功傳回0,失敗傳回-1。

資料源自:http://blog.sina.com.cn/s/blog_67e046d10100jwdo.html

下面是小的使用方法:

1 #include<iostream>
 2 #include<io.h>//_findfirst的頭檔案
 3 using namespace std;
 4 int main()
 5 {
 6     string path = "D:\\codeblocks\\wf\\";
 7     path += "*.txt";//.txt是檔案類型
 8     _finddata_t openfile;
 9     long HANDLE;//_findfirst的傳回值,若找到為0,找不到為-1
10     HANDLE = _findfirst( path.c_str(), &openfile );
11     string file_name = openfile.name;//openfile的name成員是找到的檔案名
12     cout<<file_name<<endl;
13     return 0;
14 }      

 難點3:把一行字元串按照空格分割開來

在這裡我想到了stringstream來進行資料流的重定向。下面是我對stringstream的測試:

#include<iostream>
#include<sstream>//stringstream的頭檔案
using namespace std;
int main()
{
    string str = "aaa bbb ccc ddd  eee fff";
    stringstream ss(str);
    string t;
    while(ss>>t)
        cout<<t<<endl;
    return 0;
}      

輸出為:

第1次作業

可以看到實作了用空格分割字元串。

要點1:由于功能1在輸出時要求與輸入時的順序一緻,是以我用一個vector<string>依次儲存了輸入時的字元串。在輸出時隻要依次取出即可。

1 vector<string>Vstr;
 2     map<string,int>Map;
 3     int Max_wordsize = 0;
 4     while(ss>>str)
 5     {
 6         if(str[0] >= '0' && str[0] <= '9') continue;
 7         Max_wordsize = Max(Max_wordsize,str.size());//尋找最長單詞的長度
 8         if(Map[str] == 0) Vstr.push_back(str);//把每一個單詞不重複的儲存起來
 9         Map[str]++;
10     }      

要點2:功能2在輸出時要求字元串按照字典序輸出,是以要對vector進行排序,我自定義了cmp函數。

1 bool cmp2(string a, string b)
2 {
3     return a < b;
4 }      
sort(Vstr.begin(),Vstr.end(),cmp2);      

要點3:在功能3輸出時,要求先按照單詞數量降序排序,對于數量一緻的再按照單詞的字典序升序排序,是以我建構了結構體,裡面有成員str和num,然後在功能3中把每個結構踢對象存入vector中,然後自定義排序函數。

struct Node
{
    string str;
    int num;
};      
1 for(int i = 0; i < total;++i)
2         {
3             node.str = Vstr[i];
4             node.num = Map[Vstr[i]];
5             V_node.push_back(node);
6         }      
vector<Node>V_node;
/////////////
bool cmp3(Node a, Node b)
{
    if(a.num == b.num) return a.str < b.str;
    return a.num > b.num;
}
/////////////////////////
sort(V_node.begin(),V_node.end(),cmp3);      

(1)下面是我的完整代碼,對功能1,功能2,功能3的判别是根據控制台輸入的字元串個數,如果是5段那肯定是功能3,如果不是那就是功能1或功能2,然後查詢是有-c還是-f即可判斷。

1 #include<iostream>
  2 #include<map>
  3 #include<vector>
  4 #include<sstream>
  5 #include<string>
  6 #include<fstream>
  7 #include<cstring>
  8 #include<cstdio>
  9 #include<cstdlib>
 10 #include<algorithm>
 11 #include<io.h>
 12 using namespace std;
 13 int Max(int a,int b)
 14 {
 15     return a > b ? a : b;
 16 }
 17 int Min(int a,int b)
 18 {
 19     return a < b ? a : b;
 20 }
 21 struct Node
 22 {
 23     string str;
 24     int num;
 25 };
 26 bool Get_FileName(string Path_Name,string &File_Name)
 27 {
 28     struct _finddata_t FileInfo;
 29     long Handle;
 30     Handle = _findfirst((Path_Name+"\\*.txt").c_str(),&FileInfo);//對檔案夾下的檔案篩選出.txt檔案
 31     if(Handle == -1) return 0;
 32     else
 33     {
 34         string temp = FileInfo.name;
 35         File_Name = Path_Name + "\\" + temp;
 36         return true;
 37     }
 38 }
 39 bool cmp2(string a, string b)
 40 {
 41     return a < b;
 42 }
 43 bool cmp3(Node a, Node b)
 44 {
 45     if(a.num == b.num) return a.str < b.str;
 46     return a.num > b.num;
 47 }
 48 void solve(string File_Name,int num)//實際執行檔案打開和輸出的函數,num = 5為功能2,num= -1 為功能1,num= -2為功能2
 49 {
 50     ifstream infile;
 51     string file_txt = "",str;
 52     infile.open(File_Name.c_str());//打開檔案
 53     while(getline(infile,str))
 54     {
 55         file_txt = file_txt + str + ' ';
 56     }
 57     infile.close();
 58     for(string::iterator it = file_txt.begin();it < file_txt.end();it++)//把大寫轉為小寫,并且把非字母數字轉為空格
 59     {
 60         if(*it >='A'&&*it<='Z') *it += 32;
 61         else if(*it >='a' && *it <='z') continue;
 62         else if(*it >='0' && *it <='9') continue;
 63         else *it = ' ';
 64     }
 65     stringstream ss(file_txt);
 66     vector<string>Vstr;
 67     map<string,int>Map;
 68     int Max_wordsize = 0;
 69     while(ss>>str)
 70     {
 71         if(str[0] >= '0' && str[0] <= '9') continue;
 72         Max_wordsize = Max(Max_wordsize,str.size());//尋找最長單詞的長度
 73         if(Map[str] == 0) Vstr.push_back(str);//把每一個單詞不重複的儲存起來
 74         Map[str]++;
 75     }
 76     int total = Vstr.size();
 77     if(num == -1)//功能1
 78     {
 79         cout<<"total"<<" "<<total<<endl;
 80         cout<<endl;
 81         for(int i = 0;i < total;i++)
 82         {
 83             cout<<Vstr[i];
 84             int wordsize = Vstr[i].size();
 85             for(int j = 1; j <= Max_wordsize - wordsize;++j) cout<<" ";
 86             cout<<" "<<Map[Vstr[i]]<<endl;
 87         }
 88     }
 89     else if(num == -2)//功能2
 90     {
 91         cout<<"total"<<" "<<total<<" word";
 92         if(total > 1) cout<<'s';
 93         cout<<endl;
 94         sort(Vstr.begin(),Vstr.end(),cmp2);
 95         for(int i = 0;i < total;i++)
 96         {
 97             cout<<Vstr[i];
 98             int wordsize = Vstr[i].size();
 99             for(int j = 1; j <= Max_wordsize - wordsize;++j) cout<<" ";
100             cout<<" "<<Map[Vstr[i]]<<endl;
101         }
102     }
103     else//功能3
104     {
105         Max_wordsize = 0;
106         cout<<"Total word";
107         if(total > 1) cout<<'s';
108         cout<<" is "<<total<<endl;
109         cout<<"----------"<<endl;
110         vector<Node>V_node;
111         Node node;
112         for(int i = 0; i < total;++i)
113         {
114             node.str = Vstr[i];
115             node.num = Map[Vstr[i]];
116             V_node.push_back(node);
117         }
118         sort(V_node.begin(),V_node.end(),cmp3);
119         num = Min(num,total);
120         for(int i = 0; i < num;++i)
121         {
122             Max_wordsize = Max(V_node[i].str.size(),Max_wordsize);
123         }
124         for(int i = 0;i < num;++i)
125         {
126             cout<<V_node[i].str;
127             int wordsize = V_node[i].str.size();
128             for(int j = 1; j <= Max_wordsize - wordsize;++j) cout<<" ";
129             cout<<" "<<V_node[i].num<<endl;
130         }
131     }
132 
133 }
134 int main()
135 {
136     string str_input,Path_Name,File_Name;//控制台輸入的内容,實際路徑,實際檔案名
137     bool havePath = false,haveFile = false;//判斷是有路徑還是有檔案名
138     getline(cin,str_input);
139     vector<string>input;
140     stringstream ss(str_input);//對控制台輸入的文字按照空格分割
141     string str;
142     while(ss>>str)
143     {
144         if(str == "-c") haveFile = true;//判斷是否有檔案名
145         if(str == "-f") havePath = true;//判斷是否有判斷是否有路徑
146         input.push_back(str);
147     }
148     int len = input.size();
149     if(havePath)//如果存在的是路徑
150     {
151 
152         for(int i = 0;i < len;i++)
153         {
154             if(input[i] == "-f") {Path_Name = input[i+1];break;}
155         }
156         string temp = "";
157         //因為C++'\'的轉義
158         for(string::iterator it = Path_Name.begin();it < Path_Name.end();it++)
159         {
160             if(*it == '\\') temp += "\\";
161             else temp+=*it;
162         }
163         Path_Name = temp;
164         bool is = Get_FileName(Path_Name,File_Name);//擷取檔案名
165         if(is == false) {cout<<"路徑下找不到.txt檔案!"<<endl;return 0;}//擷取失敗,輸出
166     }
167     else
168     {
169         for(int i = 0;i < len;i++)
170         {
171             if(input[i] == "-c") {File_Name = input[i+1];break;}
172         }
173     }
174     int num;
175     if(len == 5)
176     {
177         for(int i = 0;i < len ;i++)
178             if(input[i] == "-n")
179             num = atoi(input[i+1].c_str());
180     }
181     else
182     {
183         if(str_input.find("-c") != string::npos) num = -1;
184         else num = -2;
185     }
186     solve(File_Name,num);
187     return 0;
188 }
189 /*
190 wf -f D:\codeblocks\wf
191 wf -f D:\codeblocks\wf -n 3
192 */      

(2)功能測試:注:功能3四種輸入均進行了測試

功能1:

第1次作業

功能2:

第1次作業

功能3:

對四種輸入均進行了測試,均能實作

第1次作業
第1次作業
第1次作業
第1次作業

(3)自認為題目中的小細節

1)在輸出時單詞 word 後面有沒有s,我注意到了這個問題,是以我對單詞進行了計數,若大于1就加s。

if(total > 1) cout<<'s';      

2)對單詞數一列的對齊,我用一個變量維護了最大單詞長度,然後在輸出時對于其他單詞,長度比他小幾個我就補幾個空格,這樣就實作了對齊。

1 int Max_wordsize = 0;
2     while(ss>>str)
3     {
4         if(str[0] >= '0' && str[0] <= '9') continue;
5         Max_wordsize = Max(Max_wordsize,str.size());//尋找最長單詞的長度
6         if(Map[str] == 0) Vstr.push_back(str);//把每一個單詞不重複的儲存起來
7         Map[str]++;
8     }      

輸出時

1     if(num == -1)//功能1
 2     {
 3         cout<<"total"<<" "<<total<<endl;
 4         cout<<endl;
 5         for(int i = 0;i < total;i++)
 6         {
 7             cout<<Vstr[i];
 8             int wordsize = Vstr[i].size();
 9             for(int j = 1; j <= Max_wordsize - wordsize;++j) cout<<" ";//補充空格
10             cout<<" "<<Map[Vstr[i]]<<endl;
11         }
12     }      

(4)對于這次作業我自己的心得:

1)在這次作業的過程中我對自己比較滿意的是在正式編碼前将 檔案打開、指定路徑下檔案查詢、怎樣用空格分開字元串,其實在這裡面花費時間是最大的,我慶幸我這麼做了,如果一開始就正式編碼、遇到困難然後去搜尋,那麼可能你已經寫了很多了,然後如果對新的用法不熟悉,編譯會報錯,debug會很困難,而我在編碼前就通過小實驗熟悉了操作,是以在正式編碼時debug幾乎沒有花費多少時間,這算我的一個收獲吧!雖然這次我沒有犯這個錯誤但我還是要打好預防針。

2)而不足就是編碼前沒有足夠了解問題,導緻頻繁去看問題,浪費了不少時間。就建構之法中的軟體開發流程而言就不夠合格,這次讓我意識到麻雀雖小但也五髒俱全,小的軟體項目也應該按照流程走,這樣表面上花費了時間,但是在總體來看是節省不少時間。

3)GIT并沒有那麼簡單,本以為隻要了解了那幾個語句就可以了,但是在push是發生了不少錯誤,查了不少資料才解決。其實C++等語言學習也是一樣的,那些文法學習其實是最基礎的,然後就是怎樣進行組合會什麼功能,在更新就是對于各種報錯的解決,這一點是沒有捷徑的,隻有長時間的練習才能解決,沒有學習是一蹴而就的。

上一篇: 第二次作業
下一篇: 第1次作業