作业要求博客地址:https://edu.cnblogs.com/campus/nenu/2016CS/homework/2110
git仓库地址:https://git.coding.net/pipifan/wf.git
PSP阶段:
PSP阶段 | 所花时间百分比(607 / min) | 预计所花时间(440/min) | |
计划 | 43 | 40 | |
·明确需求和其他相关因素,估计每段时间成本 | 43 | 40 | |
开发 | 509 | 360 | |
·需求分析 | 35 | 40 | |
·生成设计文档 | 26 | 30 | |
·设计复审(和同学审核设计文档) | 40 | 20 | |
·代码规范(为目前的开发制定合适的规范) | 28 | 30 | |
·具体设计 | 功能一 | 18 | 10 |
功能二 | 18 | 10 | |
功能三 | 16 | 10 | |
·具体编码 | 功能一 | 58 | 40 |
功能二 | 90 | 50 | |
功能三 | 75 | 60 | |
·代码复审 | 47 | 20 | |
·测试(自测、修改代码、提交修改) | 50 | 40 | |
报告 | 55 | 40 | |
·测试报告 | 8 | 15 | |
·事后总结 | 47 | 25 |
//具体时间已修改,因为有一天的零散时间忘记算了
分析原因:主要的差距在于具体编码技术、写代码的工程习惯和工程思想。
其一、这次写PSP阶段分析是我的第一次尝试,这次让我认识了在项目中,写代码只是众多核心部分之一,文档分析,测试数据都是不可或缺的一部分。
其二、在单元测试方面自己还没有养成良好的习惯,没有做到书中的全路径覆盖等等,这也让我意识到做好一个软件是一个庞大的项目。
改进方法:自己还需多多锻炼代码能力,养成写代码的工程思想。
从项目中获得的经验
一开始看到这个需求,我感觉最大的难点还是在于对文件路径的操作。做这个项目之前,虽然我已经有一定的编程基础,但仅限于ACM那种即时编写,即时运行,并有给好的输入数据。这次从输入的不同,判断输入的是文件路径还是文件名,对我是一种全新的尝试,我从中学会了很多东西。
比如:一、通过(struct _finddata_t fileinfo)这个结构,存储有关文件的信息,并通过(fileinfo.attrib & _A_SUBDIR)这种判定方法确定该文件否为目录,还可以通过递归的算法,找到所有的文件名。
二、通过ifstream读入文件,并以文件中的字符串流形式输入。
重点代码展示
这一段代码是我从网上找到的,虽然不是我自己写的,但我觉得这一段非常值得我学习,这里面有很多我觉得很重要的知识点都是我之前没掌握的
这一段的功能是:已知一个文件路径,读入该路径下的所有文件
void get_all_file(string path , vector<string>& files , string format)//DFS找到该文件下所有文件名并放入VECTOR中 { long hFile = 0;//句柄 struct _finddata_t fileinfo; string p; if( ( hFile = _findfirst( p.assign( path ).append("\\*" + format).c_str() , &fileinfo ) ) != -1 ) { do { if( fileinfo.attrib & _A_SUBDIR ){//是否为目录 if( strcmp( fileinfo.name , "." ) != 0 && strcmp( fileinfo.name , ".." ) != 0 )//若文件名不是当前文件夹或父文件夹 get_all_file( p.assign(path).append("\\").append(fileinfo.name), files , format );//继续迭代搜索所有文件夹名 } else files.push_back( p.assign( fileinfo.name ) ); }while( _findnext( hFile , &fileinfo ) == 0 );//找下一个符合条件的文件夹 _findclose(hFile); } }
这一段代码是计算单词频率的算法部分,是我认为最精华的一部分,我通过两个map容器,一个结构体,完成了对文件名和文件路径两种不同输入方式的排序。
若单词数目为n,本算法的时间复杂度为O(nlogn)。在时间上这么优化主要多亏了map容器,让我在插入一对pair的时候把时间复杂度缩减到log级别,并且因为最后的sort也是
O(nlogn)级别的,所以整体的时间复杂度为O(nlogn)
void solve( string input_str , string s , bool have_Path , bool have_File )//计算单词频数 { map<string , ll> mp,id; string str = "" , tmp = ""; read_txt( s , str ); int file_num , cnt = 1; bool limit = Get_num( input_str , file_num ); for( auto ch : str ){ if( ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'z' ){ tmp += ch; continue; } if( !tmp.empty() ){ if( tmp[0] >= 'a' && tmp[0] <= 'z' ){ mp[tmp]++;//mp存当前字符串出现的频数 if( !id.count(tmp) ) id[tmp] = cnt++;//id存当前字符串出现的顺序 } tmp = ""; } } if( !tmp.empty() ){ if( tmp[0] >= 'a' && tmp[0] <= 'z' ) mp[tmp]++; tmp = ""; } if( !limit ){ cout<<"total "<<mp.size()<<endl; puts(""); vector<node> v; if( have_File ){//若给出的是文件名,则按出现顺序排序 for( auto it : mp ) v.push_back( node{ it.first , it.second , id[it.first] } ); sort( v.begin() , v.end() ); for( auto it : v ){ cout<<left<<setw(30)<<it.word<<it.num<<endl; } } else{//若给出的是文件路径,则按字典序排序 for( auto it : mp ){ cout<<left<<setw(30)<<it.first<<it.second<<endl; } } } else{//否则按出现频数从大到小排序 cout<<"Total words is "<<mp.size()<<endl; puts("------------------"); vector<psl> vec( mp.begin() , mp.end() ); sort( vec.begin() , vec.end() , cmp() ); for( auto it : vec ){ cout<<left<<setw(30)<<it.first<<it.second<<endl; file_num--; if( !file_num ) break; } } }
测试数据:
功能一:
功能二:
功能三:
不足之处:
这个代码其实是有BUG的,只不过我没解决:
第一:当文件路径中含有空格时,我读到一半就会跳出,因为我是利用空格作为文件名的分隔符,因此这里会有漏洞。
第二:当输入不规范时,我程序会报错,因为我是默认-f的-后第三个字符,为有效字符串的起始点,此时若多加几个空格我的读入就会出错,程序也会因此报错。