软件工程个人项目
1.Github项目地址
https://github.com/AMzzcc/WCSimulation
2.开发预计时间
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
Estimate | 估计这个任务需要多少时间 | 15 | 15 |
Development | 开发 | ||
Analysis | 需求分析(包括学习新技术) | 40 | 50 |
Design Spec | 生成设计文档 | 20 | 15 |
Design Review | 设计复审(和同事审核设计文档) | 5 | 5 |
Coding Standard | 代码规范(为目前的开发置顶合适的规范) | 5 | 5 |
Design | 具体设计 | 10 | 10 |
Coding | 具体编码 | 40 | 35 |
Code Review | 代码复审 | 20 | 15 |
Test | 测试(自我测试,修改代码,提交修改) | 40 | 50 |
Reporting | 报告 | ||
Test Report | 测试报告 | 10 | 10 |
Size Measurement | 计算工作量 | 5 | 5 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 10 | 10 |
Total | 合计 | 220 | 225 |
3.解题思路描述
实现功能如下:
基本功能列表:
-
-c file.c //返回文件 file.c 的字符数
-
-w file.c //返回文件 file.c 的词的数目
-
-l file.c //返回文件 file.c 的行数
扩展功能:
- -a 返回更复杂的数据(代码行 / 空行 / 注释行)。
高级功能:
-
基本的Windows GUI 程序操作
-
-x 支持通过图形界面选取文件
-x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。
编程语言选择:
虽然觉得用C#可能会更方便些,但出于对语言的喜爱和挑战最终选择了C++
功能所需知识分析:
1. 基本功能和扩展功能属于算法问题,是对文件信息的计算。
2. 如何在cmd运行exe时传入额外参数
3. 如何通过API调用window的资源管理器选择文件来获取文件路径
4. 如何使用C++进行文件读取
5. 软件工程的单元测试等技能
6. 采用QT的方式可以用C++编写图形化界面
这其中关键的几点问题是:
1.如何通过API调用window的资源管理器选择文件来获取文件路径,搜索花费了较长时间,筛选尝试了不少方式,
2.图形化界面显示文件信息,想法是(1)采用QT的方式可以用C++编写图形化界面,但QT基本没使用过。(2)通过C#编写Unity的脚本来实现图形化界面,Unity是一个挺方便的游戏引擎,但最后由于时间不够只好放弃了
3.软件工程相关技能的实践
4.设计实现过程
总体分析
首先需要一个负责全过程的指挥官,然后输入字符串选择功能,读取字符串保存选择的功能,跳出文件管理器选择所选文件并获取地址,读取文件并进行文件信息的计算,最后输出文件信息。
类,接口,结构体等设计
WC
1.负责指挥程序的全过程,调用其他封装好的类里的函数进行程序
2.对错误信息进行检测和输出显示
3.对最终计算的文件信息进行输出显示
CommandProcess
1.封装定义好指令信息结构体以及相关结构体
2.处理输入指令,将其打包成指令信息结构体
FIleProcess
1.通过OPENFILENAME对话框调用window资源管理器选择文件
2.获取文件路径并检查文件正确性
class file
{
public:
string GetFilePathByGUI(CommandInfo commandInfo);
//获取文件并检查文件正确性
private:
CString getFilePathByGUI();
//调用OPENFILENAME对话框
};
Function
1.封装存放所有功能的实现函数供WC调用
class function
{
public:
int CharacterCount(); //zifu
int WordCount(); //danci
int LineCount(); //hang
DLC DlcCount(); //kuozhan
function(CommandInfo commandInfo)
{
command_Info = commandInfo;
}
private:
int characterCount(CommandInfo commandInfo);
int wordCount(CommandInfo commandInfo);
int lineCount(CommandInfo commandInfo);
DLC dlcCount(CommandInfo commandInfo);
CommandInfo command_Info;
};
接口:
int main(int argc, char* argv[]); //负责接受额外传入参数
void ErrorMessage(CommandInfo commandInfo);//输出错误信息
void FileMessage(CommandInfo commandInfo, function Function); //输出文件信息
结构体
typedef struct
{
public:
bool isError=false; //是否出错
ErrorType errorType= noError; //错误类型
CommandType commandType; //命令类型
string filePath; //文件地址
void HasError(ErrorType type)
{
isError = true;
errorType = type;
}
}CommandInfo; //指令信息结构体
typedef struct
{
bool c = false;
bool w = false;
bool l = false;
bool a = false;
bool x = false;
}CommandType; //指令类型结构体
5.代码说明
通过OPENFILENAME对话框调用window资源管理器选择文件
CString file::getFilePathByGUI()
{
CString path;
OPENFILENAME open;// 公共对话框结构。
char file[MAX_PATH];// 用来保存获取文件名称的缓冲区。
ZeroMemory(&open, sizeof(OPENFILENAME)); // 初始化选择文件对话框
open.lStructSize = sizeof(OPENFILENAME);//指定这个结构的大小,以字节为单位。
open.lpstrFile = file;//打开的文件的全路径
open.lpstrTitle = "选择文件";//窗口的标题
open.lpstrFile[0] = ' '; //第一个字符串是过滤器描述的显示字符串
open.nMaxFile = MAX_PATH; //指定lpstrFile缓冲的大小,以TCHARs为单位
open.lpstrFilter = "文本文件(*.txt) *.txt C语言文件(*.c) *.c "; //打开文件类型
open.nFilterIndex = 1; //指定在文件类型控件中当前选择的过滤器的索引
open.lpstrFileTitle = NULL; // 指向接收选择的文件的文件名和扩展名的缓冲(不带路径信息)。这个成员可以是NULL。
open.nMaxFileTitle = 0; //指定lpstrFileTitle缓冲的大小,以TCHARs为单位
open.lpstrInitialDir = NULL; //指向以空字符结束的字符串,可以在这个字符串中指定初始目录。
open.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;//位标记的设置,你可以使用来初始化对话框
if (GetOpenFileName(&open)) // 显示打开选择文件对话框。
{
path = file;
cout<<path + "
";
}
return path;
}
字符计数器
int function::characterCount(CommandInfo commandInfo)
{
int charNum = 0;
ifstream ReadFile;
ReadFile.open(commandInfo.filePath, ios::in);
char str[256];
while (!ReadFile.eof() && ReadFile.getline(str, 256))
{
for (int i = 0; i < strlen(str); i++)
{
if (str[i] == ' ' || str[i] == '
' || str[i] == ' ' || str[i] == '
')
continue;
else charNum++;
}
}
ReadFile.close();
return charNum;
}
单词计数器
int function::wordCount(CommandInfo commandInfo)
{
int wordNum = 0;
int charNum=0;
ifstream ReadFile;
ReadFile.open(commandInfo.filePath, ios::in);
char str[256];
while (!ReadFile.eof() && ReadFile.getline(str, 256))
{
for (int i = 0; i < strlen(str); i++)
{
if ((str[i] >= 'a' && str[i] <= 'z' )||(str[i] >= 'A' && str[i] <= 'Z'))
{
charNum++;
continue;
}
else if(charNum>=2) //字符数大于2即为单词
{
charNum = 0;
wordNum++;
}
}
}
if(charNum>=2) //避免末尾的单词没遇到其他字符导致数量丢失
{
wordNum++;
}
ReadFile.close();
return wordNum;
}
行数计数器
int function::lineCount(CommandInfo commandInfo)
{
ifstream ReadFile;
int lineNum = 0;
char line[256];
ReadFile.open(commandInfo.filePath, ios::in);
while (!ReadFile.eof()&& ReadFile.getline(line, 256, '
'))
{
lineNum++;
}
ReadFile.close();
return lineNum;
}
扩展功能:代码行 / 空行 / 注释行等计数器
DLC function::dlcCount(CommandInfo commandInfo)
{
DLC dlc;
int lineNum = 0;
int charNum = 0;
ifstream ReadFile;
bool isNoteLine=false;
ReadFile.open(commandInfo.filePath, ios::in);
char str[256];
while (!ReadFile.eof() && ReadFile.getline(str, 256,'
'))
{
charNum = 0;
for (int i = 0; i < strlen(str); i++)
{
if (isNoteLine)//"/**/"跨行注释
{
if (str[i] == '*' && (i + 1) < strlen(str))
{
if (str[i + 1] == '/')
{
dlc.noteLine++;
isNoteLine = false;
break;
}
}
}
else
{
if (charNum >= 2)
{
dlc.codeLine++;
break;
}
}
if (str[i] == '/'&& (i + 1) < strlen(str))
{
if (str[i + 1] == '/') //单行注释
{
dlc.noteLine++;
break;
}
if (str[i + 1] == '*')
{
isNoteLine = true;
break;
}
}
if (str[i] != ' ' && str[i] != '
' ) //字符数累计
{
charNum++;
continue;
}
}
if (isNoteLine)//多行注释跨行增加
{
dlc.noteLine++;
}
lineNum++;
}
if (charNum >= 2&&!isNoteLine) //避免末尾的单词数量丢失
{
dlc.codeLine++;
}
dlc.nullLine = lineNum - dlc.codeLine - dlc.noteLine;
ReadFile.close();
return dlc;
}
6.测试代码
1.单元测试
对字符,单词,行数等等计数器进行测试
2.回归测试
(1)空文件
(2)单字符文件
(3)单个单词测试
(4)单行测试
(5)源程序测试
3. 运行测试
(1)-c
(2)-w
(3)-l
(4)-a
(5)-x
7.总结
在这次个人项目中,不仅学到了平时没有使用的windowsAPI和不熟悉的部分区域代码,还学到了规划项目的重要性,测试的重要性及如何操作,受益匪浅,还踩了不少坑,挺棒的。
其实这个项目还可以再进行以下完善:
-
实现更多要求的功能:
- 用QT采用C++/用Unity采用C# 实现图形化界面
- 增加可递归处理选中文件的目录下的子文件
- 增加通配符(模糊处理)
-
增强可扩展性:将功能类封装成可修改,可自定义
-
提高效率:比如采用引用,改善算法
-
增强安全性:添加异常检测
-
提高封装程度:利用类的成员函数的公有和私有
(C++可做的事蛮多的),但由于其他作业偷懒没做了。
参考资料:
- C/C++中main函数接受外部参数的写法,以及如何传参数:https://blog.csdn.net/selina8921/article/details/79141031
- CString:https://baike.sogou.com/v817436.htm?fromTitle=CString
- OPENFILENAME:https://baike.baidu.com/item/OPENFILENAME/1166193?fr=aladdin
- C++读取txt数据出错(乱码、数据出错、文件打不开):https://blog.csdn.net/xiongly123/article/details/80057093