第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
·明确需求和其他相关因素,估计每段时间成本 20 40
开发 490 903
·需求分析 20 60
·生成设计文档 30 0
·设计复审(和同学审核设计文档) 20 0
·代码规范(为目前的开发制定合适的规范) 10 30
·具体设计 50 60
·具体编码 300 673
·代码复审 20 40
·测试(自测、修改代码、提交修改) 40 40
报告 90 100
·测试报告 30 35
·计算工作量 30 35
·事后总结,并提出过程改进计划 30 40

 

功能模块 具体阶段 预计时间(min) 实际时间(min)
功能1 具体设计 15 20
具体编码 50 130
测试完善 10 12
功能2 具体设计 15 20
具体编码 70 246
测试完善 15 13
功能三 具体设计 20 20
具体编码 180 290
测试完善 15 15

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分析差距原因:

(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在输出时要求与输入时的顺序一致,所以我用一个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:

功能2:

功能3:

对四种输入均进行了测试,均能实现

 

 

(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++等语言学习也是一样的,那些语法学习其实是最基础的,然后就是怎样进行组合会什么功能,在升级就是对于各种报错的解决,这一点是没有捷径的,只有长时间的练习才能解决,没有学习是一蹴而就的。

posted @ 2018-09-22 11:21  稽猿丶AC  阅读(280)  评论(1编辑  收藏  举报