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()统计字符串长度,符合定义即为空白行

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

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

          架构图C++实现wc.exe程序

设计了一个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++实现wc.exe程序

测试结果:

C:UsersLDesktop软件工程wcRelease>wc.exe -c 1.cpp
字符数为:30
C:UsersLDesktop软件工程wcRelease>wc.exe -w 1.cpp
单词数为:10
C:UsersLDesktop软件工程wcRelease>wc.exe -l 1.cpp
行数为:13
C:UsersLDesktop软件工程wcRelease>wc.exe -a 1.cpp
空行数为:1
注释行数为:3
代码行数为:9
测试-d
测试结果:
C:UsersLDesktop软件工程wcRelease>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:UsersLDesktop软件工程test1.cpp
字符数为:30
单词数为:20
行数为:39
C:UsersLDesktop软件工程test113.cpp
字符数为:104
单词数为:52
行数为:75
C:UsersLDesktop软件工程test13.cpp
字符数为:146
单词数为:85
行数为:111
C:UsersLDesktop软件工程test2.cpp
字符数为:185
单词数为:114
行数为:141
C:UsersLDesktop软件工程testmain.cpp
字符数为:588
单词数为:581
行数为:417
 
 
总字符数为:1351
总单词数为:810
总行数为:417
 
代码覆盖率:100%
C++实现wc.exe程序
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表也是一个新的概念,一开始估计时间都比较少,过于乐观,直到开发和测试阶段,很多没有想到的小问题让我卡住了很长一段时间,以至于实际时间比估计时间翻了三倍。最后是让我学会了先写好大致框架再实现,没有出现以前的边写边改,最好代码很紊乱的问题。我相信一个好的程序员是在开发中成长的,这次项目为我的开发之路开了个好头。

原文链接: https://www.cnblogs.com/lylblog/p/9638337.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    C++实现wc.exe程序

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/281140

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月15日 上午5:27
下一篇 2023年2月15日 上午5:28

相关推荐