个人项目作业

一、github地址

https://github.com/Chendabaiiii/wordCounter

二、PSP表格(估计)

 

 PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

 Planning

 计划

 30

 

 Estimate

 估计这个任务需要多少时间

 30

 

 Development

 开发

 1440

 

 Analysis

 需求分析 (包括学习新技术)

 120

 

 Design Spec

 生成设计文档

 60

 

 Design Review

 设计复审 (和同事审核设计文档)

 30

 

 Coding Standard

 代码规范 (为目前的开发制定合适的规范)

 30

 

 Design

 具体设计

 60

 

 Coding

 具体编码

 1000

 

 Code Review

 代码复审

 60

 

 Test

 测试(自我测试,修改代码,提交修改)

 30

 

 Reporting

 报告

 60

 

 Test Report

 测试报告

 40

 

 Size Measurement

 计算工作量

 30

 

 Postmortem & Process Improvement Plan

 事后总结, 并提出过程改进计划

 60

 

 合计

 

 3080

 

 

三、解题思路

1. 编程语言的选择

一开始注意到此软件是一个命令行程序,可能需要用到 cmd 命令行传参来运行,之前学C语言时并没有了解过此类方法。自己本身的技术栈也只是页面上的开发,并没有学过利用前端三剑客开发桌面应用的相关技术。后来了解了一项可以结合HTML、CSS、JS开发桌面应用的Electron技术后,又想起有个遍历文件/选择文件的功能,这个时候js可能没有优势,想到可能会踩很多坑,于是决定用C++/C作为技术栈边学边用开发此软件。

四、设计实现过程

1. 代码的组织

首先我把主函数文件和工具函数分开,在主函数进行工具函数的调用;

工具函数中的核心函数有:

int getWordNumInLine(char* str);    // 计算单行单词个数
void trim(string& str);          // 去除字符串的头尾空格
void characterCount(string fileName);   // 计算字符个数
void wordCount(string fileName);         // 计算单词个数
void lineCount(string fileName);           // 记算行数
void complexLineCount(string fileName);    // 返回更复杂的数据(代码行 / 空行 / 注释行)
bool judgeComment(string line, bool& isInComment);   // 判断是不是在注释行内

五、代码说明

getWordNumInLine

// 计算文件的字符个数
void characterCount(string fileName) {
    int charNum = 0;        // 记录字符数目
    char line[256];
    fstream file;                // 文件流
    file.open(fileName, ios::in);            // 采用读取方式打开文件
    while (!file.eof())
    {
        file.getline(line, 256, '\n');    // 该行字符达到256个或遇到换行就结束
        int len = strlen(line);
        charNum += len;
        for (int i = 0; i < len; i++) {
            // 如果是中文则要减1,因为中文算是双字节
            if (line[i] < 0 && line[i + 1] < 0) {  
                charNum--;
                i++;
            }  
        }
    }
    file.close();
    cout << "该文件共有" << charNum << "个字符" << endl;
};

characterCount

// 计算单行单词个数
int getWordNumInLine(char* str) {
    int cnt = 0, i = 0;
    int len = strlen(str);
    for (i = 0; i < len - 1; i++) {
        // 字母 + 非字母 则为一个单词的结束
        if (isalpha(str[i]) && (!isalpha(str[i + 1]))) cnt++;
    }
    // 以两个字母结束的情况
    if (isalpha(str[i])) cnt++;
    return cnt;
}

wordCount

// 计算文件的单词个数
void wordCount(string fileName) {
    int wordNum = 0;        // 记录单词数目
    char line[256];
    fstream file;                // 文件流
    file.open(fileName, ios::in);            // 采用读取方式打开文件
    while (!file.eof())
    {
        file.getline(line, 256, '\n');    // 该行字符达到256个或遇到换行就结束
        wordNum += getWordNumInLine(line);
    }
    file.close();
    cout << "该文件共有" << wordNum << "个单词" << endl;
}

lineCount

// 计算文件的行数
void lineCount(string fileName) {
    int lineNum = 0;        // 记录行数目
    char line[256];
    fstream file;                // 文件流
    file.open(fileName, ios::in);            // 采用读取方式打开文件
    while (!file.eof())
    {
        file.getline(line, 256, '\n');    // 该行字符达到256个或遇到换行就结束
        lineNum++;
    }
    file.close();
    cout << "该文件共有" << lineNum << "" << endl;
}

judgeComment

// 判断是不是在注释内
// 返回true表示该行是注释行
bool judgeComment(string line, bool& isInComment) {
    regex pattern1("^[{}]?[\\s]*\\/\\/[^\\n]*");  // 为"//"、"{...//"、"}...//"的行级注释
    regex pattern2("^\\*\\/[//s]*[{}]?$");                // 为"*/"、"*/...{"、"*/...}"
    regex pattern3("\\/\\*[^\\*^\\/]*\\*\\/");            // 匹配 /**/
    regex pattern4("^[{}]?[//s]*\\/\\*[^\\n]*");    // 匹配 "/*..."、"{/*..."、"}/*..."
    if (regex_match(line, pattern1)) return true;        // 是行级注释
    if (regex_match(line, pattern2)) {
        isInComment = false;  // 没在注释内但是该行算注释
        return true;                    
    } 
    // 如果在注释内,而且该行找不到 */ 块注释结束行
    if (isInComment && line.find("*/") == string::npos) {
        return true; 
    }
    string replaceStr = regex_replace(line, pattern3, "");  // 去除字符串中所有 /**/对
    if (regex_match(replaceStr, pattern4)) {
        isInComment = true;
        return true;
    }
    return false;
}

complexLineCount

// 返回更复杂的数据(代码行 / 空行 / 注释行)
void complexLineCount(string fileName) {
    int codeLine = 0,                        // 代码行
            emptyLine = 0,                    // 空行
            commentLine = 0;                // 注释行
    bool isInComment = false;      // 是否在注释块里
    bool isEmptyLine = false;        // 是否是空行的标志
    regex isEmpty("^[{}]?$");   // 是否是空行的正则
    char line[256];
    fstream file;                // 文件流
    file.open(fileName, ios::in);            // 采用读取方式打开文件
    while (!file.eof())
    {
        isEmptyLine = false;
        file.getline(line, 256, '\n');    // 该行字符达到256个或遇到换行就结束
        int len = strlen(line);
        string strLine(line);       // 将char数组转为string
        trim(strLine);
        // 注释块里面的空行不包括
        if (!isInComment && regex_match(line, isEmpty)) {
            emptyLine++;    // 不在注释块内且长度小于1则为空行
            isEmptyLine = true;
        } 
        // 如果是注释行
        if (judgeComment(strLine, isInComment)) {
            commentLine++;
        } else if(!isEmptyLine){
            // 如果不是注释行而且不是空行,那就是代码行
            codeLine++;
        }
    }
    file.close();
    cout << "该文件共有" << endl << codeLine << "代码行" << endl << emptyLine << "空行" << endl << commentLine << "注释行" << endl;
}

main方法

// 主函数
// argc 是命令参数数目
// argv 接收命令参数字符串, argv[0] 默认为exe文件路径
int main(int argc, char** argv)
{
    if (argc < 3) {
        cout << "参数有误,请输入正确的参数" << endl;
        return 0;
    }
    // 需要外传入两个参数,一个是模式,一个是文件名
    string mode = argv[1];
    string fileName = argv[2];
    setlocale(LC_ALL, "chs");
    switch (mode[1]) {
        // 字符计数
        case 'c': {
            characterCount(fileName);        
            break;
        }
        // 单词计数
        case 'w': {
            wordCount(fileName);                
            break;
        }
        // 行数
        case 'l': {
            lineCount(fileName);                
            break;
        }
        // 返回更复杂的数据(代码行 / 空行 / 注释行)
        case 'a': {    
            complexLineCount(fileName);
            break;
        }
        default: {
            break;
        }
    }
    return 0;
}

六、测试运行

功能实现:本程序只实现了 -c、-w、-l、-a的功能指令

测试使用样例

int a;//what
/**/int b;

// 6
void test() {

  for (;;) {
  }

} // aaaaa
/*
*/

测试结果

-c(计算字符数)

 -w(计算单词数)

 -l(计算行数)

 -a(复杂行数计算)

七、PSP表格(实际)

 

 PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

 Planning

 计划

 30

 20

 Estimate

 估计这个任务需要多少时间

 30

 20

 Development

 开发

 1440

 780

 Analysis

 需求分析 (包括学习新技术)

 120

 200

 Design Spec

 生成设计文档

 60

 0

 Design Review

 设计复审 (和同事审核设计文档)

 30

 20

 Coding Standard

 代码规范 (为目前的开发制定合适的规范)

 30

 20

 Design

 具体设计

 60

 10

 Coding

 具体编码

 1000

 780

 Code Review

 代码复审

 60

 60

 Test

 测试(自我测试,修改代码,提交修改)

 30

 60

 Reporting

 报告

 60

 60

 Test Report

 测试报告

 40

20

 Size Measurement

 计算工作量

 30

 30

 Postmortem & Process Improvement Plan

 事后总结, 并提出过程改进计划

 60

 20

 合计

 

 3080

 2100

 



八、项目小结

第一次使用c语言开发一个小小小程序,有很多做的不好的地方。code review之后觉得自己的代码封装性不强,代码覆盖率不高,唯有仅仅将功能函数与主函数进行分离。

由于以前很少接触书本以外的API,要实现一些功能需要查阅好些资料。此次遇到比较大的困难是注释行的计算,由于思考过后发现将会有很多的情况,于是后来考虑使用正则表达式来匹配,但由于正则接触的不多,平时也比较少使用,基本上就忘了,经过这个功能的实现,也相当于重温了一下正则表达式。也经过了这次项目的开发,发现自己技术上还有太多太多的缺陷。由于技术的限制、其他不可抗力因素以及自己的拖延症,而导致最终只能实现这几个小小的功能。

也让我清楚的知道了自己的编程之路还很长,于是我将反思后在下个项目付出更多的努力!

 

posted @ 2020-03-15 23:49  Chendabaiiii  阅读(149)  评论(0编辑  收藏  举报