个人项目开发PSP实践-MyWCprj

news/2024/11/24 9:53:21/

MyWCprj.exe

Github仓库地址

1. What is MyWCprj.exe?

wc是linux下一个非常好用的代码统计小工具,可以通过 -c 、-w 、-l等选项分别进行对指定文件的代码字符数、词组数、行数统计。

应学校软件工程课程的PSP个人项目开发实践需要,特此尝试自己写一个用c++实现的wc(MyWCprj.exe)。

该程序仅能确保统计.c .cpp .h文件代码数的正确性。

语法

MyWCprj.exe [parameter] [fileOrDir_name1] [fileOrDir_name2] ……

功能参照

MyWCprj.exe -c file.c    //输出文件 file.c 的字符数

MyWCprj.exe -w file.c    //输出文件 file.c 的词的数目 

MyWCprj.exe -l file.c    //输出文件 file.c 的行数

MyWCprj.exe -a file.c    //输出文件 file.c

MyWCprj.exe -s fileDir    //递归处理目录fileDir及其子目录中代码文件,无其他参数时仅列举目录fileDir及其子目录中代码文件(.c .cpp .h),可配合一个或多个 -c -w -l -a 一起处理。

MyWCprj.exe -l file1.c file2.c file3.c   //输出file1.c、file2.c、file3.c的行数

MyWCprj.exe -s -l DirPath1 DirPath2 DirPath3  //递归输出DirPath1、DirPath2、DirPath3目录及其子目录下所有代码文件的行数

 

测试示例

参考中典型的源代码文件 CommonSrcCode.cpp:

 1 #include"WChead.h"2 3 4 int main(int argc,char *argv[])5 {6     /*cout << "ar num=" << argc << endl;7     for (int i = 0; i < argc; i++)8     {9         cout << "No." << i << " ";
10         cout << "ar =" << argv[i] << endl;
11     }
12     system("PAUSE");*/
13 
14     if (argc < 2)
15     {
16         cout << "" << endl;
17         return -1; 18  } 19 20  class_WordCal myWC; 21  myWC.workMain(argc, argv); 22 23 //system("PAUSE"); 24 return 0; 25 }

测试:

# 运行环境:windows 10 pro,ver 1803,powershell
# 使用 dir 命令,可看出 I:\TmpCode 下的示例代码文件
PS ..\MyWCprj\Debug> dir I:\TmpCode
Directory: I:\TmpCode
Mode                LastWriteTime         Length Name------
#典型的源代码文件 CommonSrcCode.cpp    
-a----        9/14/2018   9:34 AM            384 CommonSrcCode.cpp    
#空代码文件 EmptyCode.cpp
-a----        9/14/2018   7:29 PM              0 EmptyCode.cpp    
#仅有字符'a'的代码文件 Onechar.cpp
-a----        9/14/2018   7:32 PM              1 Onechar.cpp    
#仅有字符串“#pragma once”的代码文件 Oneline.cpp    
-a----        9/14/2018   7:34 PM             12 Oneline.cpp
#仅有字符串“#define” 的代码文件 Oneword.cpp    
-a----        9/14/2018   7:32 PM              7 Oneword.cpp # 使用 .\MyWCprj.exe -s -w -c -l -a I:\TmpCode 组合 # 可看出 MyWCprj.exe 递归输出当前目录下的代码文件对应信息 PS ..\MyWCprj\Debug> .\MyWCprj.exe -s -w -c -l -a I:\TmpCode list_size: 5 No.0 I:\TmpCode\CommonSrcCode.cpp: The Num of chars: 360 I:\TmpCode\CommonSrcCode.cpp The Num of words: 45 I:\TmpCode\CommonSrcCode.cpp The Num of Lines: 24 I:\TmpCode\CommonSrcCode.cpp The Num of Effective lines: 8 I:\TmpCode\CommonSrcCode.cpp The Num of Empty lines: 9 I:\TmpCode\CommonSrcCode.cpp The Num of Comment lines: 8 I:\TmpCode\CommonSrcCode.cpp No.1 I:\TmpCode\EmptyCode.cpp: The Num of chars: 0 I:\TmpCode\EmptyCode.cpp The Num of words: 0 I:\TmpCode\EmptyCode.cpp The Num of Lines: 0 I:\TmpCode\EmptyCode.cpp The Num of Effective lines: 0 I:\TmpCode\EmptyCode.cpp The Num of Empty lines: 1 I:\TmpCode\EmptyCode.cpp The Num of Comment lines: 0 I:\TmpCode\EmptyCode.cpp No.2 I:\TmpCode\Onechar.cpp: The Num of chars: 1 I:\TmpCode\Onechar.cpp The Num of words: 1 I:\TmpCode\Onechar.cpp The Num of Lines: 0 I:\TmpCode\Onechar.cpp The Num of Effective lines: 1 I:\TmpCode\Onechar.cpp The Num of Empty lines: 0 I:\TmpCode\Onechar.cpp The Num of Comment lines: 0 I:\TmpCode\Onechar.cpp No.3 I:\TmpCode\Oneline.cpp: The Num of chars: 12 I:\TmpCode\Oneline.cpp The Num of words: 2 I:\TmpCode\Oneline.cpp The Num of Lines: 0 I:\TmpCode\Oneline.cpp The Num of Effective lines: 1 I:\TmpCode\Oneline.cpp The Num of Empty lines: 0 I:\TmpCode\Oneline.cpp The Num of Comment lines: 0 I:\TmpCode\Oneline.cpp No.4 I:\TmpCode\Oneword.cpp: The Num of chars: 7 I:\TmpCode\Oneword.cpp The Num of words: 1 I:\TmpCode\Oneword.cpp The Num of Lines: 0 I:\TmpCode\Oneword.cpp The Num of Effective lines: 1 I:\TmpCode\Oneword.cpp The Num of Empty lines: 0 I:\TmpCode\Oneword.cpp The Num of Comment lines: 0 I:\TmpCode\Oneword.cpp

 

2. PSP实践

PSP2.1表格

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划430120
Estimate估计这个任务需要多少时间3015
Development开发400300
Analysis需求分析 (包括学习新技术)15045
Design Spec生成设计文档2040
Design Review设计复审 (和同事审核设计文档)2060
Coding Standard代码规范 (为目前的开发制定合适的规范)划1085
Design具体设计30120
Code Review代码复审3060
Test测试(自我测试,修改代码,提交修改)30120
PReporting报告11050
Test Report测试报告3060
Size Measurement计算工作量2040
Postmortem & Process Improvement Plan事后总结, 并提出过程改进计划6070
合计计划430680

 

 

3. 分析思路

由于曾经有过在linux上逆向分析二进制程序的经验,对于ltrace 、wcobjdumpptracegdb等二进制分析与调试工具有一定的了解与使用经历,所以对于wc功能也有一定的了解。

于是乎,去GNUWC命令行工具的C源码,简洁且优雅,在性能与可读性上有非常好的平衡。据其分析之,于是就以C++的方式实现了-c-w-l的功能,并初步封装入c++自定义的类class_WordCal中。

对于-a功能,要求分析出待统计代码文件的空行、注释行、有效代码行。先是与室友沟通,结果很天马行空,基于字符判断的分析模式利用代码实现很困难且可读性不佳,于是在google中查阅与编译原理、代码预处理相关的网页与资料,结果在csdn论坛中获得启发。依然是源码注释行统计.请进来看看,感谢之至!。可利用状态机的思想:

 

假定一开始是非注释状态,读入一个字符,如果不是/,保持非注释状态继续读入字符,
如果字符是/,读入下一个字符,如果不是*,保持非注释状态,如果是*,进入注释状态;
在注释状态,类似的读入字符,如果不是*,保持注释状态继续读入字符,
如果字符是*,读入下一个字符,如果不是/,保持注释状态,如果是/,退出注释状态进入非字符状态。

于是自己依据该思想创建了一个结构体:

 

1 typedef struct {    
2     bool bEffective;        //有效行标识
3     bool bComm1;            //    "//"    注释行标识
4     bool bComm2;            //    "/**/"    注释行标识
5 }AllStatus;    //高级统计状态标识结构体

 

具体的功能思路的代码实现见Github代码有。

至于-s功能,由于对windows的文件操作不算太熟悉,于是在网上查找目录递归遍历的思路,基本上就是调用诸如_findfirst 、_findnext的库函数,逐个获取目录下的子目录或文件的路径并存之于对应的string类的vector,遇到子目录则对子目录路径进行递归遍历。如此反复,最终可获得一个关于所有子目录路径的vector<string>,和关于所有代码文件路径的vector<string>。然后根据每一个代码文件路径实现指定的分析与信息输出功能即可。难点在于如何实现递归遍历上,这对于提升编程能力与启发编程思想来说是一个很好的实践。

 

 

4. 大体设计

 

  1. 高级统计(-a)状态结构体AllStatus:

    1 typedef struct {
    2     bool bEffective;        //有效行标识
    3     bool bComm1;            //    "//"    注释行标识
    4     bool bComm2;            //    "/**/"    注释行标识
    5 }AllStatus;    //高级统计状态标识结构体
  2. 数据统计类 class_data:
     1 class class_Data2 {3 public:4     int iNumOfChar;                //存储当前文件的字符数5     int    iNumOfWord;                //存储当前文件的单词数6     int iNumOfLine;                //存储当前文件的行数7     int iNumOfEffectiveLine;    //存储当前文件的有效代码行数8     int iNumOfEmptyLine;        //存储当前文件的空行数9     int iNumOfCommentLine;        //存储当前文件的注释行数
    10     string strTmpRowStr;        //临时存储刚从文件读入的一行代码
    11     AllStatus allStatus;        //高级统计状态机
    12     
    13     class_Data()
    14     {
    15         iNumOfChar = 0; 16 iNumOfWord = 0; 17 iNumOfLine = 0; 18 iNumOfEffectiveLine = 0; 19 iNumOfEmptyLine = 0; 20 iNumOfCommentLine = 0; 21 allStatus.bComm1 = false; 22 allStatus.bComm2 = false; 23 allStatus.bEffective = false; 24 allStatus.bEmpty = false; 25  } 26 27 ~class_Data() 28  { 29 30  } 31 32 };
  3. 总功能类设计class_WordCal:
     1 class class_WordCal : public class_Data2 {3 public:4     bool bOpChar;                        //字符统计输出开关                        5     bool bOpLine;                        //行数统计输出开关6     bool bOpWord;                        //词组统计输出开关7     bool bOpRecursion;                    //递归统计开关8     bool bOpAll;                        //更复杂数据统计开关9     int iFileNameIndex;                    //存储文件名的stringVector中带操作的文件名下标
    10     ifstream tmpifs;                    //存储当前打开文件的文件读操作类
    11     vector<string> vStrFileOrDirName;    //存储当前文件名的stringVector
    12     vector<string> vStrCodeFilePath;    //存储文件或目录路径的stringVector
    13     string strFilePath;                    //存储当前文件的路径
    14 
    15 
    16     void Option(int argc, char *argv[]);            //功能选择函数
    17     void Display(int Index);                        //统计输出函数
    18     void StringSkip(string s, int &pos);            //当s中存在' ' '\t' '\n' '\r'时,将pos置于该字符之后
    19     void myFileFind(string DirPath);                //对传入目录路径下的所有代码文件(.c .cpp .h) 录入至 vStrCodeFilePath 中
    20     void DirList(vector<string> &vStr, const string strPath);    //从strPath指定的路径中递归查找代码文件,并录入至vStrCodeFilePath中
    21 
    22     int getword(FILE *fp);                            //从输入流中获取下一个词组,读取至文件结尾或异常情况出现时返回0,返回1则为其他情况
    23     Status SetIfstream(vector<string> vStr, int Index);    //设置打开文件的文件读操作类
    24     //Status CountFromFile(int Index);                //(旧代码,不兼容-s统计功能)对vStrFileOrDirName中第Index个文件名对应文件进行代码统计(-l -w -c)
    25     //Status CountFromFileAll(int Index);            //(旧代码,不兼容-s统计功能)对vStrFileOrDirName中第Index个文件名对应文件进行高级代码统计(-a)
    26 
    27     Status CountFromDir(int Index);                    //对vector<string>中第Index个目录下的所有代码文件进行递归统计(-s)
    28     Status CountFromFile(vector<string> vStr, int Index);//对vector<string>中第Index个文件名对应文件进行代码统计(-l -w -c)
    29     Status CountFromFileAll(vector<string> vStr, int Index);//对vector<string>中第Index个文件名对应文件进行高级代码统计(-a)
    30     void Display(vector<string> vStr, int Index);    //vector<string>中第Index个文件名输出统计函数
    31     Status EmptyCheck(string s, int pos);            //空行检查与统计
    32     
    33     void CountMain();                                //统计功能入口
    34     void workMain(int argc, char *argv[]);            //类主功能函数入口
    35 
    36 };

     

5. 关键功能伪码

 

1. -c -w -l :

 1 wc_basic_function(FileName)2 {3     File *fp = fopen(FileName, "r");4     char c;5     bool bEnd = false;6     while(bEnd != true) 7  { 8 while ((c = getc(fp)) != EOF) 9  { 10 if (isalpha(c)) //调用stdarg.h中的isalpha(),判断是否为词组,是则词组数++; 11  { 12 iNumOfWord++; 13 break; 14  } 15 iNumOfChar++; 16 if ((c) == '\n') 17  { 18 iNumOfLine++; 19  } 20  } 21 while (c != EOF) //由于词组判断完后上一个循环结束,此循环将继续统计行数及字符数。 22  { 23 iNumOfChar++; 24 if ((c) == '\n') 25  { 26 iNumOfLine++; 27  } 28 if (!isalpha(c)) 29  { 30 break; 31  } 32 c = getc(fp); 33  } 34 bEnd = (c != EOF); 35  } 36 }

2. -a:

  1 function_GetFileAllInfo()2 {3     Status tmpStatus;4     allStatus.bComm1 = allStatus.bComm2 = allStatus.bEffective = false;    //高级统计状态机初始化5     iNumOfEffectiveLine = iNumOfEmptyLine = iNumOfCommentLine = 0;    //代码行数、空行数、注释行数置零初始化6     while (!tmpifs.eof())7     {8         pos = 0; 9 allStatus.bEffective = false; 10 getline(tmpifs, strTmpRowStr); //读取文件中的一行代码 11 12  StringSkip(strTmpRowStr, pos); 13 14 tmpStatus = EmptyCheck(strTmpRowStr, pos); //空行检查与统计 15 if (en_true == tmpStatus) 16  { 17 continue; 18  } 19 else if (en_fail == tmpStatus) 20  { 21 return en_fail; 22  } 23 24 while (1) 25  { 26 if (false == allStatus.bComm2 27 && '/' == strTmpRowStr[pos] 28 && '/' == strTmpRowStr[pos + 1]) //读取到"//"的情况 29  { 30 if (false == allStatus.bEffective) 31  { 32 iNumOfCommentLine++; 33  } 34 else 35  { 36 iNumOfEffectiveLine++; 37  } 38 break; 39  } 40 41 if (false == allStatus.bComm2 42 && '}' == strTmpRowStr[pos] 43 && '/' == strTmpRowStr[pos + 1] 44 && '/' == strTmpRowStr[pos + 2]) //读取到"}//"的情况 45  { 46 if (false == allStatus.bEffective) 47  { 48 iNumOfCommentLine++; 49  } 50 else 51  { 52 iNumOfEffectiveLine++; 53  } 54 break; 55  } 56 57 if (false == allStatus.bComm2 58 && '/' == strTmpRowStr[pos] 59 && '*' == strTmpRowStr[pos + 1]) //读取到"/*"的情况 60  { 61 pos += 2; 62 allStatus.bComm2 = true; 63 continue; 64  } 65 if (true == allStatus.bComm2) // "/**/"的过滤处理 66  { 67 if ('*' == strTmpRowStr[pos] && '/' == strTmpRowStr[pos + 1]) //读到"*/"时的状态处理 68  { 69 pos++; 70 allStatus.bComm2 = false; 71 iNumOfCommentLine++; 72  } 73 else if ('\0' == strTmpRowStr[pos]) //读取至行末时的处理 74  { 75 if (true == allStatus.bEffective) //若此时为有效代码状态,则代码行数++。例如 printf("hello\n"); /*this is a hello print*/ 76  { 77 iNumOfEffectiveLine++; 78  } 79 else //否则,此处代表纯注释行,则注释行数++ 80  { 81 iNumOfCommentLine++; 82  } 83 break; 84  } 85 pos++; 86 continue; 87  } 88 89 90 if ('\0' == strTmpRowStr[pos]) //运行至行末 91  { 92 if (allStatus.bEffective == false) 93  { 94 if (allStatus.bComm1 || allStatus.bComm2) //纯注释行 95  { 96 ++iNumOfCommentLine; 97  } 98  } 99 else if (allStatus.bEffective == true) 100  { 101 ++iNumOfEffectiveLine; 102  } 103 break; 104  } 105 pos++; 106 allStatus.bEffective = true; 107  } 108  } 109  tmpifs.close(); 110 }

3. -s:

递归算法伪码:

 1 class cal{2     ...3     vector<string> vFilePath;4     vector<string> vDirPath;5     ...6     wc_GetFile_main(string strDirPath)    //递归遍历目录,并将所有代码文件路径传入FilePath中7  { 8 string tmpStr; 9 DirList(vDirPath, strDirPath + "\\*"); //从strDirPath + "\\*"指定的路径中递归查找代码文件,并录入至vFilePath中;将strDirPath的子目录名传入vDirPath中。 10 for (i = 0; i < vDirPath.size(); ++i) 11  { 12 tmpPath = strDirPath + "\\" + vDirPath[i]; //子目录路径拼接。 13 myFileFind(tmpPath); //对子目录进行递归操作。 14  } 15  } 16 17 DirList(verctor<string> &vStr,string strPath) //获取当前目录下的子目录名与代码文件路径,分别录入到vStr与vFilePath中。 18  { 19 vector<string> vStrTmp; 20  _finddata_t fileDir; 21 long lfDir = _findfirst(strPath.c_str(), &fileDir); 22 int tmpPos; 23 24 if (-1l == lfDir) //尚未实现具体的文件类型识别,仅进行_findfirst是否成功的判断 25  { 26 cout << "DirList: "<< strPath <<" File or dir not found!" << endl; 27  } 28 else 29  { 30 string tmp = strPath; 31 tmp.erase(tmp.size() - 1, 1);//去除路径中的'*' 32 do 33  { 34 string FileOrDirName(fileDir.name); 35 if (FileOrDirName.find('.') == -1) //目录则将目录名传入vStrTmp中 36  { 37  vDirPath.push_back(fileDir.name); 38  } 39 else if( 40 ((tmpPos = FileOrDirName.find(".cpp")) != -1 && FileOrDirName[tmpPos + 4] == '\0') //精确匹配.cpp字串 41 || ((tmpPos = FileOrDirName.find(".c")) != -1 && FileOrDirName[tmpPos + 2] == '\0') //精确匹配.c字串 42 || ((tmpPos = FileOrDirName.find(".h")) != -1 && FileOrDirName[tmpPos + 2] == '\0') //精确匹配.h字串 43 )//代码文件则将其路径传入vStrCodeFilePath中 44  { 45 vFilePath.push_back(tmp + FileOrDirName); 46  } 47 } while (_findnext(lfDir, &fileDir) == 0); 48  } 49  _findclose(lfDir); 50 vStr = vStrTmp; 51  } 52  ... 53 };

在获得到所有代码文件路径的vector<string> vFilePath后,逐一对vFilePath内各个路径进行代码统计与信息输出即可,简单的迭代就可以实现。

 

总结

  1. 前期的准备工作非常重要,知识学习与储备、类似的程序框架的参考、算法的评估、按需求设计功能模块等等,都是为中期代码开发提供方向与实现基础的,不重视前期工作的话在中期代码开发与后期的测试、文档撰写方面要吃很大的亏,代码体现的思路与实现方式也难以简洁优雅。这次实践中我就是吃了这个亏,导致在具体代码实现时做了很多前期需要做的工作,流程很乱,效率不高。

  2. 注意待统计代码文件的编码类型,utf-8在此程序中表现良好。曾在测试环节中遇到对unicode编码的文件进行统计,算法失败地一塌糊涂,统计结果与实际也相差甚远。在接近有一个半小时的时间里都是在debug,试图在算法上找错误的根源,无果。最终发现是unicode编码下宽字符串的问题,换用utf-8后表现良好。

  3. 此次的时间较赶,感觉代码质量还不高,找机会整理好思路,按PSP的流程进行代码重构。

转载于:https://www.cnblogs.com/HelloGaveu/p/9649580.html


http://www.ppmy.cn/news/668900.html

相关文章

PSP 和 Xbox 盗版论坛 250 万数据被盗

据 softpedia 网站消息&#xff0c;Xbox360ISO.com 和 PSPISO.com 在 2015 年年底遭受了匿名黑客的攻击&#xff0c;导致 250万网站用户账号被窃&#xff0c;包含电子邮件、IP地址、网站用户名称和密码&#xff0c;但最近这批数据才在网上浮出水面。 这两家网站均以提供 PSP 和…

PSP 笔记 ——未完

PSP介绍&#xff1a; psp是为了得到 编程可控&#xff1b; 改善你的能力&#xff1b; 对自己评估&#xff1b; 错误和解决 参考网址&#xff1a;www.sei.cmu.edu/tsp/psp 为了更好地学习PSP&#xff0c;我们需要测试方法和激励手段&#xff0c;如下&#xff1a; the Psp 课程…

PSP播放wav文件

网上找了很长时间&#xff0c;都没找到很好的播放wav文件的例子 后来在一个国外一个叫http://forums.qj.net的论坛上找到这么一篇文章 [SAMPLE] Simple Wav Loader. 心里那个激动啊&#xff0c;点进去看&#xff0c;源码附件因为年代久远已经失效了&#xff0c;好在作者还贴了代…

linux系统装psp,psp上装Linux

psp上装Linux 发布时间:2006-03-21 00:29:34来源:红联作者:linux110.com Bochs x86模拟器被Matan移植到PSP上的消息。现在已经证实这个消息是真的&#xff0c;并且将给PSP能完成的任务带来极大的拓展&#xff0c;现在包括FreeDOS、Linux、DLX Linux、NetBSD、OpenBSD、PicoBSD、…

JAVA线程池参数详解

官方翻译&#xff1a; 线程池中的七大参数如下&#xff1a; &#xff08;1&#xff09;corePoolSize&#xff1a;线程池中的常驻核心线程数。 &#xff08;2&#xff09;maximumPoolSize&#xff1a;线程池能够容纳同时执行的最大线程数&#xff0c;此值大于等于1。 &#xff0…

vue3+ts-setup语法糖(props)

vue3ts-setup语法糖(props) vue3 新增了组合式api的语法糖&#xff0c;相应的props&#xff0c;也会有相应的变化。下面是我总结的三种。 <script setup lang"ts"> // 第一种&#xff1a; 使用这种方式可以设置props的 默认值 和 类型 和 是否可选 // const …

仿QQ空间文章列表+评论查询

业务如图 需要查询一个文章列表&#xff0c;包括文章摘要&#xff0c;标题&#xff0c;发布者信息&#xff0c;以及对于的文章评论 实现思路一&#xff1a; ①&#xff1a;先查询文章列表 ②&#xff1a;遍历文章列表结果集&#xff0c;依次查出文章的评论 优点&#xf…

模仿qq空间或朋友圈发布动态、评论动态、回复评论、删除动态或评论的功能(上)...

我们大部分人都发过动态&#xff0c;想必都知道发动态、回复评论、删除动态的整个过程&#xff0c;那么作为初学者&#xff0c;要模仿这些功能有点复杂的&#xff0c;最起码表的关系得弄清楚~~ 先把思路理一下&#xff1a; &#xff08;1&#xff09;用户登录&#xff0c;用ses…