个人项目 源程序特征统计程序(C++)
零、GitHub地址
https://github.com/King-Authur/Word-count
一、项目的相关要求
wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。
实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
具体功能要求:
程序处理用户需求的模式为:wc.exe [parameter] [file_name]
基本功能列表:
wc.exe -c file.c     //返回文件 file.c 的字符数
wc.exe -w file.c //返回文件 file.c 的词的数目
wc.exe -l file.c //返回文件 file.c 的行数
扩展功能:
-s   递归处理目录下符合条件的文件。
-a   返回更复杂的数据(代码行 / 空行 / 注释行)。
空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。
代码行:本行包括多于一个字符的代码。
注释行:本行不是代码行,并且本行包括注释。
一个有趣的例子是有些程序员会在单字符后面加注释:
 } //注释
在这种情况下,这一行属于注释行。
[file_name]: 文件或目录名,可以处理一般通配符。
高级功能:
-x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。
需求举例:
  wc.exe -s -a *.c
返回当前目录及子目录中所有*.c 文件的代码行数、空行数、注释行数。
简化后的要求
基本功能
支持 -c
支持 -w
支持 -l
扩展功能
支持 -s 参数
支持 -a 参数
支持各种文件的通配符(*,?)
高级功能
基本的Windows GUI 程序操作
支持通过图形界面选取文件
支持通过图形界面展现文件的信息
二、遇到的困难和解决方法
(一)如何使用命令行运行程序
查阅相关资料后,总结了知识点和方法,并写了一遍博客
博客链接
(二)如何实现基础功能
首先定好判断的标准
字符:字母、计算机字符
单词:由一个或一个以上的连续字母所构成的字符串
行:空行与非空行的总和
代码行:非空行且非注释行
空行:由空格、回车、tab、} 构成的行
注释行:含有 \\ 并且它不被包含在两个双引号中
思路是,按照该标准一步一步实现代码,计算出结果即可。
(三)如何递归查询文件夹 以及 通配符 ?和 * 如何实现**
这两个功能可以一起实现。
关于这部分,我查阅相关资料后总结了知识点和方法,并写了一遍博客
博客链接
递归文件夹部分使用_findfirst();和_findnext();函数实现的。
至于通配符,因为cmd输入带有通配符的文件路径作为参数时,会自动完成对文件路径的通配符补充,所以无法通过用cmd运行来验证通配符功能是否实现。
但是由于通配符是用_findfirst();和_findnext()函数实现的,程序中的代码经过严格调试,可以认为无误,逻辑上通配符功能是实现了的。
思路上:对于传入的文件路径,例如:D:\c++\WC\*.cpp
会把它拆成两部分 D:\c++\WC\ 和 *.cpp
递归D:\c++\WC\下的所有文件夹,在当前目录的所有文件夹都遍历后,对当前目录下非文件的文件进行通配符 *.cpp的匹配,计算信息后,输出各个匹配成功的文件的结果。
(四)图形界面的实现
能够弹出简单的图形界面
(五)错误输入的检验
有判断文件是否存在,路径是否正确,在输入错误的文件路径后,会报错并终止程序。
(六)正则表达式的判断
没有实现,待更新。
三、关键代码 和 设计说明
设计图
采用了面向过程的设计

目前实现了以下几个函数
void get_flag(int argc, char **argv);
void calculate_information(char *file_path);
bool is_char(char ch);
void display();
关键代码
1、void calculate_information(char *file_path);
该函数用于计算出所有题目中要我们求的数据,包括字符、单词、行、代码行、空行、注释行的数目。
void calculate_information(char *file_path){
    FILE* fp = fopen(file_path, "r");//只读打开文件路径指向的文件
    if(fp == NULL){//检测是否是有效路径
        printf("!!!错误!!!\n请检查文件路径是否正确\n");
        exit(-1);//关闭程序
    }
    char buffer[maxn_word_of_line];//存取当前行数据的缓存区
    while(!feof(fp))//是否到文件尾
    {
        buffer[0] = '\0';
        fgets(buffer, maxn_word_of_line, fp);//一整行读取
        res.line++;
        int len = strlen(buffer);
        bool left_blank = true, is_Empty = true;
        for(int i = 0; i < len; i++)
        {
           if(is_char(buffer[i]))///如果是非空字符
            {
                res.character++;///那么字符数要增加
                is_Empty = false;///并且这一行不为空
            }
            if(isalpha(buffer[i]))///当它是字母时
            {
                if(left_blank)///当左边被其他字符隔开时
                {
                    res.word++;///单词数目+1
                    left_blank = false;
                }
            }
            else///当前字符不是字母
            {
                left_blank = true;///则视为它是隔开单词的字符
            }
        }
        if(is_Empty)///这一行为空行
        {
            res.empty_line++;
        }
        else///这一行非空,判断一下是否为注释行
        {
            bool left_quote = false;///左双引号
            bool slanting_bar = false;///斜杆
            bool explain_flag = false;///注释行的标记
            ///过滤掉包含于字符串输出的“假注释”,例如:printf("//");
            for(int i = 0; i < len; i++)
            {
                if(buffer[i] == '"')///如果是双引号
                {
                    if(!left_quote)///如果没有左边匹配的双引号
                    {
                        left_quote = true;
                    }
                    else
                    {
                        left_quote = false;///左边有可以匹配的双引号
                    }
                }
                if(buffer[i] == '/' && buffer[i + 1] == '/')///如果有连续的两个反斜杠
                {
                    slanting_bar = true;
                }
                if(!left_quote && slanting_bar)///如果斜杠没有被包含在字符串的输出中,则这一行必定为注释行
                {
                    res.explain_line++;
                    explain_flag = true;
                    break;
                }
            }
            if(!explain_flag)///不是注释行,就是代码行
            {
                res.code_line++;
            }
        }
    }
}
该函数会逐行读取整个文件,每行遍历两次即可计算出改行的所有信息,信息存放在结构体内,不断更新直到读取到文件尾。
输出时根据传入的参数决定输出哪些内容。
2、````
四、测试运行
基础部分与扩展部分中的 -a 一起测试
1、测试只有两行空白行的文件

无误
2、测试只含有两个单词、十个字符的文件

无误
3、测试一份简单的代码

检验可知
printf("\\");没有被误认为注释行
printf("Hello world\n");//Hello world 和}//注释正确的被识别为注释行
其他数据的结果也无误
测试文件路径
4、输入错误的文件路径

会有错误提示
5、递归文件夹功能

可以看到,在输入的文件目录下的 main.cpp 和 该目录的bin文件下的 main.cpp这两个同名的文件(内部代码不同)都输出了信息,递归功能无误。
五、PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) | 
|---|---|---|---|
| Planning | 计划 | 40 | 30 | 
| · Estimate | · 估计这个任务需要多少时间 | 15 | 10 | 
| Development | 开发 | 360 | 400 | 
| · Analysis | · 需求分析 (包括学习新技术) | 60 | 40 | 
| · Design Spec | · 生成设计文档 | 20 | 20 | 
| · Design Review | · 设计复审 (和同事审核设计文档) | 10 | 10 | 
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 20 | 
| · Design | · 具体设计 | 30 | 30 | 
| · Coding | · 具体编码 | 120 | 100 | 
| · Code Review | · 代码复审 | 20 | 20 | 
| · Test | · 测试(自我测试,修改代码,提交修改) | 30 | 40 | 
| Reporting | 报告 | 30 | 35 | 
| · Test Report | · 测试报告 | 20 | 20 | 
| · Size Measurement | · 计算工作量 | 10 | 10 | 
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 40 | 
| 合计 | 905 | 825 | 
六、项目小结
1、不熟悉面向对象编程,只使用了面向过程编程
2、没有识别/**/注释的代码
3、递归文件夹下的文件 和 图形界面 功能尚未完成
4、提交时GitHub网页炸了
5、代码实现和测试时出现了一些小错误,导致花费了更多的时间
6、还在学习Windows 编程
7、后续还会继续更新和完善
8、一开始设计时,因为过多的考虑了效率上的问题,而没有让结果有足够高的精准度。
七、参考来源
[1] 软件工程个人项目作业
[2] 项目博客模板
[3] 《构建之法》
[4] VS2015安装与C++进行简单单元测试
[5] C++利用 _findfirst与_findnext查找文件的方法
个人项目 源程序特征统计程序(C++)的更多相关文章
- 震惊!!!源程序特征统计程序——基于python getopt库
		项目github地址:https://github.com/holidaysss/WC PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟 ... 
- 个人项目——wc源程序特征统计
		这一次要做的项目是wc——统计程序文件特征的命令行程序. 根据需求需求得到的模式为:wc.exe [parameter][filename] 在[parameter]中,用户通过输入参数与程序交互,需 ... 
- “人向猿进阶”之软件工程第三课----WORDCOUNT.EXE统计程序
		---恢复内容开始--- WC项目要求 这个项目要求写一个命令行程序,模仿已有的wc.exe的功能,并加以扩充,给出某程序设计源语言文件的字符数.单词数和行数.给实现一个统计程序,它能正确统计程序文件 ... 
- wc 统计程序
		WC项目要求 这个项目要求写一个命令行程序,模仿已有的wc.exe的功能,并加以扩充,给出某程序设计源语言文件的字符数.单词数和行数.给实现一个统计程序,它能正确统计程序文件的字符数.单词数.行数,以 ... 
- 这台计算机上缺少此项目引用的 NuGet 程序包-缺少的文件是 ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props
		异常处理汇总-开发工具 http://www.cnblogs.com/dunitian/p/4522988.html 协助开发里面总有几个是极简爱好者,但是呢删了不该删的就会影响项目开发,下面看下完 ... 
- 项目 XXX 的 NuGet 程序包还原失败:找不到“xxx”版本的程序包“xxx”
		项目 XXX 的 NuGet 程序包还原失败:找不到“xxx”版本的程序包“xxx” 编译新下载的代码出错 修改包管理器的源为 http://www.nuget.org/api/v2/ .重试后成功 ... 
- c++英文单词频度统计程序
		英文单词频度统计程序(c++版) 写一个程序,分析一个文本文件(英文文章)中各个次出现的频率,并且把频率最高的十个词打印出来. 分析过程: (1) 简单设想大致分为两大步骤: 1.经过文本文件的读操 ... 
- C# 统计程序执行时间
		随便写写,小马哥勿怪 最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷. 话说马云年轻的时候也是屌丝一枚,有图为证 现 ... 
- 项目   Web 的 NuGet 程序包还原失败: 找不到“1.0.0”版本的程序包“Microsoft.Net.Compilers”。。 0
		项目 Web 的 NuGet 程序包还原失败: 找不到“1.0.0”版本的程序包“Microsoft.Net.Compilers”.. 0 使用vs的NutGet包管理器时,另一台电脑从svn下载 ... 
随机推荐
- AList的具体实现 #CS61B-sp18-2.5
			实现一个Array based list,其功能包括获取长度size,添加元素至最后addLast,得到元素get和去除最后一个元素. 设计思路及其实现: 我们都知道在获取数据的时候,直接调用缓存里面 ... 
- 使用 eval(input()) 的便利
			输入列表或者字典时使用eval可以自动转换为其类型 2020-06-18 
- 从键盘输入一个字符串(长度不超过30),统计字符串中非数字的个数,并将统计的结果显示在屏幕上,用EXE格式实现。
			问题 从键盘输入一个字符串(长度不超过30),统计字符串中非数字的个数,并将统计的结果显示在屏幕上,用EXE格式实现. 源程序 data segment hintinput db "plea ... 
- Fragment为什么须要无参构造方法
			日前在项目代码里遇到偷懒使用重写Fragment带参构造方法来传参的做法,顿生好奇,继承android.support.v4.app.Fragment而又不写无参构造方法不是会出现lint错误编译不通 ... 
- 【小白学AI】八种应对样本不均衡的策略
			文章来自:微信公众号[机器学习炼丹术] 目录 1 什么是非均衡 2 8种解决办法 2.1 重采样(四种方法) 2.2 调整损失函数 2.3 异常值检测框架 2.4 二分类变成多分类 2.5 EasyE ... 
- ArrayList源码解析,老哥,来一起复习一哈?
			前言 JDK源码解析系列文章,都是基于JDK8分析的,虽然JDK14已经出来,但是JDK8我还不会,我... 类图 实现了RandomAccess接口,可以随机访问 实现了Cloneable接口,可以 ... 
- SpringBoot2.x下RabbitMQ的并发参数(concurrency和prefetch)
			RabbitMQ消费端配置 spring: rabbitmq: host: localhost port: 5672 username: guest password: guest listene ... 
- day24:多态&魔术方法__new__&单态模式
			目录 1.多态 2.__new__魔术方法 2.1 关于魔术方法__new__ 2.2 基本语法 2.3 __new__ 触发时机快于构造方法 2.4 __new__ 和 __init__ 参数一一对 ... 
- 重温这几个屌爆的Python技巧!
			我已经使用Python编程有多年了,即使今天我仍然惊奇于这种语言所能让代码表现出的整洁和对DRY编程原则的适用.这些年来的经历让我学到了很多的小技巧和知识,大多数是通过阅读很流行的开源软件,如Djan ... 
- git使用-分支管理
			1.查看分支 git branch 2.创建分支 git branch name 3.切换分支 git checkout name 4.合并分支上的内容到master分支 切换到master分支上 g ... 
