软件工程个人项目

软件工程个人项目

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.解题思路描述

实现功能如下:

基本功能列表:

  1. -c file.c //返回文件 file.c 的字符数

  2. -w file.c //返回文件 file.c 的词的数目

  3. -l file.c //返回文件 file.c 的行数

扩展功能:

  1. -a 返回更复杂的数据(代码行 / 空行 / 注释行)。

高级功能:

  1. 基本的Windows GUI 程序操作

  2. -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)*.txtC语言文件(*.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和不熟悉的部分区域代码,还学到了规划项目的重要性,测试的重要性及如何操作,受益匪浅,还踩了不少坑,挺棒的。
​ 其实这个项目还可以再进行以下完善:

  1. 实现更多要求的功能:

    • 用QT采用C++/用Unity采用C# 实现图形化界面
    • 增加可递归处理选中文件的目录下的子文件
    • 增加通配符(模糊处理)
  2. 增强可扩展性:将功能类封装成可修改,可自定义

  3. 提高效率:比如采用引用,改善算法

  4. 增强安全性:添加异常检测

  5. 提高封装程度:利用类的成员函数的公有和私有

(C++可做的事蛮多的),但由于其他作业偷懒没做了。

参考资料:

  1. C/C++中main函数接受外部参数的写法,以及如何传参数:https://blog.csdn.net/selina8921/article/details/79141031
  2. CString:https://baike.sogou.com/v817436.htm?fromTitle=CString
  3. OPENFILENAME:https://baike.baidu.com/item/OPENFILENAME/1166193?fr=aladdin
  4. C++读取txt数据出错(乱码、数据出错、文件打不开):https://blog.csdn.net/xiongly123/article/details/80057093
原文地址:https://www.cnblogs.com/AMzz/p/12500738.html