zoukankan      html  css  js  c++  java
  • C++实现wc.exe程序

    github项目地址:https://github.com/insomniali/wc

    • 基本功能
      •          wc.exe -c file     统计文件file的字符数  【实现】
      •          wc.exe -w file    统计文件file的词的数目  【实现】
      •          wc.exe -l file      统计文件file的行数  【实现】
    • 扩展功能
      •     wc.exe -d file      递归显示目录下符合条件的文件  【实现】
      •         wc.exe -s file       递归统计目录下符合条件的文件的总字符,词和行数  【实现】
      •         wc.exe -a file       统计文件file的空白行,注释行,代码行  【实现】
    • 高级功能
      •      wc.exe -x            图形化界面  【未实现】

    设计思路

              根据需求,程序主要功能可分为两部分,一是接收输入指令并解析,二是对文件执行指令所对照的操作。通过程序内置参数,再调用相应类方法即可满足需求。

               字符统计函数:

                           定义:除转义字符,数字及字母外所有字符

                           方法:通过getline()获取一行数据,再由字符的ASCII码来判断

                   单词统计函数:

                           定义:“a-z”,"A-Z"组成的单个字母或多个连续字母组成的单词

                           方法:通过getline()获取一行数据,再由字符的ASCII码来判断,在统计完第一个字母后置continuectrl为真,直到统计到第一个不符合定义的字符

                  行数统计函数

                           定义:通过getline()判断,不为空则为有效行

                           方法:grtline()

                  空白,注释,代码行数统计函数

                           定义:

                                   空白行:字符串长度为0或长度为1,字符为(;,{,})之一

                                   注释行:以“{//”,“}//”,“;//”,“//”为字符串开头的行

                                   代码行:除空白行,注释行以外所有行

                          方法:

                                   空白行:用string.length()统计字符串长度,符合定义即为空白行

                                   注释行:获取字符串前三位字符,根据结果是否符合定义判断

                                   代码行:总行数减去空白行数及注释行数

              架构图

    设计了一个WCclass类,类成员变量是int类型的各种统计数据,类成员方法则是统计各种数据

    代码说明

     主函数main()

    int main(int argc ,char* argv[]) {
        string filepath = "C:\\Users\\L\\Desktop\\软件工程\\test";   //测试路径
        string filename;
        string temp;
        string operateword;
        string paramater=argv[1];
        WCclass * test=new WCclass();        
        for (int i = 2; i < argc + 1; i++) 
        {
                temp = filepath;
                filename = argv[i];
                filename = temp + "\\" + filename;
                test->fileoperate(filename, paramater);
        }   
    detele test;
    return 0; }

    指令解析与文件操作函数()

    void WCclass::fileoperate(string filename, string paramater) 
    {
        ifstream fin(filename.c_str());              //获取文件名,打开输入流
    
    
        char ch;                                    //每次判断一个字符
        string str;                                 //保存行数据
        char **strs;                                //保存字符串str以“ ”分割的字符串
                                                    //可输入参数类型
        string paramater1 = "-c";
        string paramater2 = "-w";
        string paramater3 = "-l";
        string paramater4 = "-d";
        string paramater5 = "-s";
        string paramater6 = "-a";
    
        strs = (char**)malloc(sizeof(char) * 256);
        
        while (getline(fin, str))
        {
            int strcount = 0;
            rowcount();
    
            if ((str.length() == 0) || ((str.length() == 1)&& (str[strcount] == (char)"{")) || ((str.length() == 1) && (str[strcount] == (char)"}")))
            {
                emptyrowcount();
                continue;
            }
    
            strs[strcount] = strtok((char*)str.c_str(), " ");
    
            while (strs[strcount] != NULL)
            {
                strcount++;
                strs[strcount] = strtok(NULL, " ");
            }
    
            for (int i = 0; i < strcount; i++) 
            {
                string temp = strs[i];
                int continuectrl = 0;//控制单词连续
                int dqmoff = 0;//是否是;,{,}
                int flag = 0;////是否相邻
                for (int j = 0; j < temp.length(); j++) 
                {
                    ch = temp[j];
                    if ((ch >= 33 && ch <= 47) || (ch >= 58 && ch <= 64) || (ch >= 91 && ch <= 96) || (ch >= 123 && ch <= 126))
                    {
                        charcount();
                        continuectrl = 0;
                        //注释行//的情况
                        if (ch == 47)
                        {
                            if (j == 0)
                            {
                                flag = 1;
                            }
                            else if (j == 1)
                            {
                                if (flag == 1)
                                {
                                    flag = 0;
                                    noterowcount();
                                }
                            }
                        }
                        //注释行({//,}//,;//)的情况
                        if ((ch == 59) || (ch == 123) || (ch == 125))
                        {
                            if (j == 0) {
                                dqmoff = 1;
                                continue;
                            }
                        }
                        if (dqmoff == 1)
                        {
                            if (ch == 47)
                            {
                                if (flag == 1)
                                {
                                    flag = 0;
                                    noterowcount();
                                }
                                else flag = 1;
                            }
                            else
                            {
                                dqmoff = 0;
                            }
                        }
                        
                    }
                    else if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122))
                    {
                        if (continuectrl == 1)continue;
                        continuectrl = 1;
                        wordcount();
                    }
                    else if (ch >= '0'&&ch <= '9')
                    {
                        digitcount();
                    }
                    else
                    {
                        cnwordcount();
                    }
                }
            }
        }
        if (paramater == paramater1)std::cout << "字符数为:" << charnums + cnwordnums / 2 << endl;
        else if (paramater == paramater2)std::cout << "单词数为:" << wordnums << endl;
        else if (paramater == paramater3)std::cout << "行数为:" << rownums << endl;
        else if (paramater == paramater4)listFiles(filename.c_str());
        else if (paramater == paramater5) 
        {
            filegroup = (string*)malloc(sizeof(string) * 256);
            filegroup=listFiles(filename.c_str(), filegroup);            //获取递归处理文件的文件名数组
            for (int i = 0; filegroup[i].length() != 0; i++) {
                std::cout << filegroup[i] << endl;
                fileoperate(filegroup[i], paramater1);
                fileoperate(filegroup[i], paramater2);
                fileoperate(filegroup[i], paramater3);
            }
            std::cout << endl;
            std::cout << "总字符数为:" << charnums + cnwordnums / 2 << endl;
            std::cout << "总单词数为:" << wordnums << endl;
            std::cout << "总行数为:" << rownums << endl;
        }
        else if (paramater == paramater6) 
        {
            std::cout << "空行数为:" << emptyrow << endl;
            std::cout << "注释行数为:" << noterow << endl;
            std::cout << "代码行数为:" << rownums-emptyrow-noterow << endl;
        }
        
        fin.clear();
        fin.close();
        free(strs);
    }

     递归处理函数ListFiles()

    void listFiles(const char * dir)
    {
        char dirNew[200];
        strcpy(dirNew, dir);
    
        intptr_t handle; //intptr_t是为了跨平台,其长度总是所在平台的位数,用来存放地址。 
        _finddata_t FileInfo; // _finddata_t 是用来存储文件各种信息的结构体。
    
        handle = _findfirst(dirNew, &FileInfo);  //第一个参数为文件名,可以用"*.*"来查找所有文件,也可以用"*.cpp"来查找.cpp文件。第二个参数是_finddata_t结构体指针。若查找成功,返回文件句柄,若失败,返回-1。
        if (handle == -1)        // 检查是否成功
            return;
    
        do
        {
            if (FileInfo.attrib & _A_SUBDIR) //如果文件是目录
            {
                if (strcmp(FileInfo.name, ".") == 0 || strcmp(FileInfo.name, "..") == 0)  //如果文件名和当前目录和父目录相同则跳过,否则会只输出当前目录
                    continue;
                
                std::cout << FileInfo.name << "\t<目录>\n";
    
                // 在目录后面加上"\\"和搜索到的目录名进行下一次搜索
                strcpy(dirNew, dir);
                strcat(dirNew, "\\");
                strcat(dirNew, FileInfo.name);
    
                listFiles(dirNew);
            }
            else
                std::cout << FileInfo.name << "\t" << "\n";
        } while (_findnext(handle, &FileInfo) == 0); //_findnext第一个参数为文件句柄,第二个参数同样为_finddata_t结构体指针。若查找成功,返回0,失败返回 - 1。
    
        _findclose(handle);    // 关闭搜索句柄
    }
    
    std::string* listFiles(const char * dir,std::string* str)
    {
        char dirNew[200];
        strcpy(dirNew, dir);
        int filenum = 0;
        std::string* temp = new std::string[50];
        int partcount = 0;
        std::string filepath;
        char**filepathpart;
        filepathpart = (char**)malloc(sizeof(char) * 256);
        std::string filepathtemp = dir;
        
    
        intptr_t handle; //intptr_t是为了跨平台,其长度总是所在平台的位数,用来存放地址。 
        _finddata_t FileInfo; // _finddata_t 是用来存储文件各种信息的结构体。
    
        handle = _findfirst(dirNew, &FileInfo);  //第一个参数为文件名,可以用"*.*"来查找所有文件,也可以用"*.cpp"来查找.cpp文件。第二个参数是_finddata_t结构体指针。若查找成功,返回文件句柄,若失败,返回-1。
        if (handle == -1)        // 检查是否成功
            return nullptr;
        
        filepathpart[partcount] = strtok((char*)filepathtemp.c_str(), "\\");
    
        while (filepathpart[partcount] != NULL)
        {
            partcount++;
            filepathpart[partcount] = strtok(NULL, "\\");
        }
    
        for (int i = 0; i < partcount - 1; i++) {
            filepath += filepathpart[i] ;
            filepath += "\\";
        }
        
        do
        {
            if (FileInfo.attrib & _A_SUBDIR) //如果文件是目录
            {
                if (strcmp(FileInfo.name, ".") == 0 || strcmp(FileInfo.name, "..") == 0)  //如果文件名和当前目录和父目录相同则跳过,否则会只输出当前目录
                    continue;
    
                std::cout << FileInfo.name << "\t<目录>\n";
    
                // 在目录后面加上"\\"和搜索到的目录名进行下一次搜索
                strcpy(dirNew, dir);
                strcat(dirNew, "\\");
                strcat(dirNew, FileInfo.name);
    
                listFiles(dirNew);
    
            }
            else {
                temp[filenum] = filepath;
                temp[filenum] += FileInfo.name;
                filenum++;
                std::cout << FileInfo.name << "\t" << " \n";
            }
        } while (_findnext(handle, &FileInfo) == 0); //_findnext第一个参数为文件句柄,第二个参数同样为_finddata_t结构体指针。若查找成功,返回0,失败返回 - 1。
    
        _findclose(handle);    // 关闭搜索句柄
        return temp;
    }

    测试运行

    测试-c,-w,-l,-a

    测试文件:1.cpp

    测试结果:

    C:\Users\L\Desktop\软件工程\wc\Release>wc.exe -c 1.cpp
    字符数为:30
    C:\Users\L\Desktop\软件工程\wc\Release>wc.exe -w 1.cpp
    单词数为:10
    C:\Users\L\Desktop\软件工程\wc\Release>wc.exe -l 1.cpp
    行数为:13
    C:\Users\L\Desktop\软件工程\wc\Release>wc.exe -a 1.cpp
    空行数为:1
    注释行数为:3
    代码行数为:9
    测试-d
    测试结果:
    C:\Users\L\Desktop\软件工程\wc\Release>wc.exe -d *.*
    1.cpp
    113.cpp
    13.cpp
    2.cpp
    555     <目录>
    main.cpp
    wcclass.h
    测试-s
    测试结果:
    1.cpp
    113.cpp
    13.cpp
    2.cpp
    main.cpp
    C:\Users\L\Desktop\软件工程\test\1.cpp
    字符数为:30
    单词数为:20
    行数为:39
    C:\Users\L\Desktop\软件工程\test\113.cpp
    字符数为:104
    单词数为:52
    行数为:75
    C:\Users\L\Desktop\软件工程\test\13.cpp
    字符数为:146
    单词数为:85
    行数为:111
    C:\Users\L\Desktop\软件工程\test\2.cpp
    字符数为:185
    单词数为:114
    行数为:141
    C:\Users\L\Desktop\软件工程\test\main.cpp
    字符数为:588
    单词数为:581
    行数为:417
     
     
    总字符数为:1351
    总单词数为:810
    总行数为:417
     
    代码覆盖率:100%
    PSP

    PSP2.1

    Personal Software Process Stages

    预估耗时(分钟)

    实际耗时(分钟)

    Planning

    计划

    30

    45

    · Estimate

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

    10

    10

    Development

    开发

    100

    300

    · Analysis

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

    60

    30

    · Design Spec

    · 生成设计文档

    20

    20

    · Design Review

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

    20

    100

    · Coding Standard

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

    20

    10

    · Design

    · 具体设计

    10

    10

    · Coding

    · 具体编码

    10

    10

    · Code Review

    · 代码复审

    10

    100

    · Test

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

    30

    200

    Reporting

    报告

    10

    30

    · Test Report

    · 测试报告

    10

    30

    · Size Measurement

    · 计算工作量

    10

    10

    · Postmortem & Process Improvement Plan

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

    30

    20

    合计

     

    380

    925

     项目总结
                  这次项目让我感受最大的在于测试方面,之前写代码没有作过多的测试。这次学习到单元测试,代码覆盖率等方法和概念,可以应用到以后的程序开发中。其次,psp表也是一个新的概念,一开始估计时间都比较少,过于乐观,直到开发和测试阶段,很多没有想到的小问题让我卡住了很长一段时间,以至于实际时间比估计时间翻了三倍。最后是让我学会了先写好大致框架再实现,没有出现以前的边写边改,最好代码很紊乱的问题。我相信一个好的程序员是在开发中成长的,这次项目为我的开发之路开了个好头。
  • 相关阅读:
    Mysql 触发器
    Mysql 的变量
    Mysql 事务
    重置mysql管理员密码
    mysql数据备份
    Mysql的联合查询
    Mysql的存储引擎
    数据库的视图
    数据库子查询
    数据库外键
  • 原文地址:https://www.cnblogs.com/lylblog/p/9638337.html
Copyright © 2011-2022 走看看