软件工程个人项目

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

实现功能如下:

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

基本功能列表:

  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] = '\0'; //第一个字符串是过滤器描述的显示字符串
    open.nMaxFile = MAX_PATH;  //指定lpstrFile缓冲的大小,以TCHARs为单位
    open.lpstrFilter = "文本文件(*.txt)\0*.txt\0C语言文件(*.c)\0*.c\0\0";  //打开文件类型
    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 + "\n";
    }
    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] == '\r' || str[i] == '\t' || str[i] == '\n')
                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, '\n'))
    {
        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,'\n'))
    {
        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] != '\t' && str[i] != '\r' )  //字符数累计
            {
                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.单元测试

​ 对字符,单词,行数等等计数器进行测试
软件工程个人项目 随笔 第1张

2.回归测试

(1)空文件
软件工程个人项目 随笔 第2张

软件工程个人项目 随笔 第3张

(2)单字符文件

软件工程个人项目 随笔 第4张

软件工程个人项目 随笔 第5张
(3)单个单词测试

软件工程个人项目 随笔 第6张
软件工程个人项目 随笔 第7张
(4)单行测试

软件工程个人项目 随笔 第8张

软件工程个人项目 随笔 第9张

(5)源程序测试

软件工程个人项目 随笔 第10张

软件工程个人项目 随笔 第11张

3. 运行测试

(1)-c

软件工程个人项目 随笔 第12张

(2)-w

软件工程个人项目 随笔 第13张

(3)-l

软件工程个人项目 随笔 第14张
(4)-a

软件工程个人项目 随笔 第15张

(5)-x

软件工程个人项目 随笔 第16张
软件工程个人项目 随笔 第17张

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
扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄