个人项目wc.exe的功能实现(C++)

GitHub地址:https://github.com/includebug/wc.exe

 PSP2.1表格

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

 20

 30

· Estimate

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

 360

 600

Development

开发

 300

 800

· Analysis

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

 50

 200

· Design Spec

· 生成设计文档

 15

 45

· Design Review

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

 20

 50

· Coding Standard

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

 15

 30

· Design

· 具体设计

 40

 60

· Coding

· 具体编码

 240

 600

· Code Review

· 代码复审

 40

 60

· Test

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

 30

 55

Reporting

报告

 15

 25

· Test Report

· 测试报告

 10

 25

· Size Measurement

· 计算工作量

 20

 25

· Postmortem & Process Improvement Plan

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

 10

 15

合计

 

 1185

 1935

 

题目描述

Word Count

  实现一个简单而完整的软件工具(源程序特征统计程序)。

  进行单元测试、回归测试、效能测试,在实现上述程序的过程中使用相关的工具。

  进行个人软件过程(PSP)的实践,逐步记录自己在每个软件工程环节花费的时间。

WC 项目要求

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 文件的代码行数、空行数、注释行数。

遇到的问题:

困难描述:

  1.没怎么接触过文件的操作

  2.对_finddata_t的学习

做过哪些尝试:

  上csdn跟博客园等上面查找相关的资料,然后自己创建一个项目,把他们的代码下载下载下来玩,尝试着去弄明白那些函数的特性跟使用的注意事项。在设计时本来是直接一个文件写完的,把那几个功能封装成一个函数,后来想着把这些东西封装成一个类,可以保存程序后的结果,但是写着写着就变形了,想到了怎么去改,但是时间成本太高,也想过把测试的结果写入txt的,没弄.。接触了下MFC和QT,但是来不及去弄了。如何解决通配符的问题。

是否解决:

  通配符是在看到别人的在文件下搜索所有符合的文件(不能查找子文件夹)的代码基础上加上自己写的一个递归函数完成的。在边查边写时解决了路径问题,期间忘了’/‘在输出时要用“//”,在寻找同学的帮助下解决。

有何收获:

  学会了文件的操作和模糊查找文件的操作,有些功能看似很难实现,实际上写完再回头看也不难,就是写代码的不规范或者对函数的不熟悉导致了很多问题,还是要多打代码,做多点实践。

解题思路:

  对文件的操作都选择为对文件的每一行进行操作,统计。

 

代码

下面就是我在Filecount.h定义的类

class Filecount
{
public:
	Filecount();
	void recount(); //把private存的数据清空
	~Filecount();
	void wordcount(string &filename); //单词数计算
	void charcount(string &filename); //字符数计算
	void linecount(string &filename); //所有的行数计算,包含代码行注释行等的操作
	void codelinecount();
	void emptylinecount();
	void notelinecount();
	int search(string &srcPath, string &filename,string s[],int &i); //在指定文件夹内搜索符合条件的文件,用string s[] 返回
	void filesearch(string &srcPath, string &filename,string s[],int &i); //
	void filecount(string filename, string order);

private:

	int words;    //单词数
	int lines;    //总行数
	int charnums; //字符数
	int emptylines;  //空行
	int notelines;  //注释行
	int codelines;  //代码行
};

  

行数统计

void Filecount::linecount(string &filename)
{
	string str;
	int count = 0;
	ifstream file(filename);
	if (!file.is_open())
	{
		cout << "文件名可能有错,无法打开" << endl;
		return;
	}
	while (!file.eof())
	{
		getline(file, str);//从文件中读取一行
		lines++;
		remove(str.begin(), str.end(), ' ');   //这个算法函数在algorithm头文件中,删除一行中的空格
		remove(str.begin(), str.end(), '\t');  //删除一行中的制表符,因为制表符和空格都是空的
		string notechar = "//";
		string::size_type idx;
		if (str.size() <= 1)           //清除空格后,这一行的长度小于等于1就是空行
			emptylinecount();
		else if ((idx = str.find(notechar)) != string::npos)  //如果在这一行找到//就是注释行
			notelinecount();
		int count = 0;
		for (int j = 0; (j < str.size()) && (count <= 2); j++)          //一行中多余2个英文字母为代码行
		{
			if ((str[j] >= 65 && str[j] <= 90) || (str[j] >= 97 && str[j] <= 122))
				count++;
		}
		if (count >= 2)
			codelinecount();
	}
	file.close();
}

  

单词统计

void Filecount::wordcount(string &filename)
{
	string str;
	bool isWord = false;
	ifstream file(filename);
	if (!file.is_open())
	{
		cout << "文件名可能有错,无法打开" << endl;
		return;
	}
	int count = 0;
	while (!file.eof())   //判断条件为file时,会重复读最后一行,最后一行为回车时正常行数
	{
		getline(file, str);//从文件中读取一行
		isWord = false;              //重置,错误原因,没有回到false导致计算单词数少了,
					     //每行末尾置isWord为true,导致下一行第一个单词不能计算
		for (int i = 0; i < str.size(); i++)
		{
			if (str[i] == ' ' || str[i] == '\n' || str[i] == '(' || str[i] == ')' || str[i] == '\0') //单词末尾的标志
			{
				isWord = false;
			}
			else
			{
				if (isWord == false)         //isWord为true证明单词继续,遇到单词末尾的标志就结束
				{
					isWord = true;
					words++;
				}
			}
		}
	}
	file.close();
}

  

字符数统计

void Filecount::charcount(string &filename)
{
    string str;
    ifstream file(filename);
    if (!file.is_open())
    {
        cout << "文件名可能有错,无法打开" << endl;
        return;
    }
    int count = 0;
    while (!file.eof())   //判断条件为file时,会重复读最后一行,最后一行为回车时正常行数
    {
        getline(file, str);//从文件中读取一行
        for (int i = 0; i < str.size(); i++)
        {
            if (str[i] == ' ' || str[i] == '\n' || str[i] == '\0')
                ;     //如果是这些字符什么都不做
            else
                charnums++;
        }
    }
    file.close();
}

 

查找符合条件的文件

int Filecount::search(string &srcPath, string &filename, string s[], int &i)     //在指定文件夹搜索符合的文件,不能查子文件夹的
{
    long handle; //用于查找句柄
    struct _finddata_t fileinfo; //文件信息的结构体

    string Name = srcPath + "\\" + filename;      //路径加上模糊搜索文件名

    handle = _findfirst(Name.c_str(), &fileinfo); //第一次查找
    if (-1L == handle)
        return 1;
    else
    {
        s[i] = srcPath + "\\" + fileinfo.name;
        i++;
    }
        
    while (!_findnext(handle, &fileinfo))
    {
        s[i] = srcPath + "\\" + fileinfo.name;
        i++;
    }
    _findclose(handle);//别忘了关闭句柄
    return 0;
}

void Filecount::filesearch(string &srcPath, string &filename, string s[], int &i)  //搜索文件夹内所有符合条件的文件
{
    if (srcPath == "")
        return;

    _finddata_t FileInfo;

    string startFindPath = srcPath + "\\*";       //文件路径下的所有文件

    long handle = _findfirst(startFindPath.c_str(), &FileInfo); //

    search(srcPath, filename, s, i);                    //在这个文件夹下搜索符合的文件

    if (handle == -1L)
        return; 
    //逐层递进搜索子目录
    do {
        if (FileInfo.attrib == _A_SUBDIR) //判断是否为子文件夹
        {
            if ((strcmp(FileInfo.name, ".") != 0) && (strcmp(FileInfo.name, "..") != 0))  //屏蔽这两个文件,不知道为什么
            {
                string newPath = srcPath + "\\" + FileInfo.name;
                filesearch(newPath,filename,s , i);
            }
        }

    } while (_findnext(handle, &FileInfo) == 0);

    _findclose(handle);
}

 

对命令的解析

void Filecount::filecount(string filename, string order)
{
	if (order.compare("-c") == 0)
	{
		charcount(filename);
		cout << "文件的字符数为" << charnums << endl;
	}
	else if (order.compare("-w") == 0)
	{
		wordcount(filename);
		cout << "文件的单词数为" << words << endl;
	}
	else if (order.compare("-l") == 0)
	{
		linecount(filename);
		cout << "文件的行数为" << lines << endl;
	}
		
	else if (order.compare("-a") == 0)
	{
		linecount(filename);
		cout << "文件的空行" << emptylines << endl;
		cout << "文件的注释行" << notelines << endl;
		cout << "文件的代码行" << codelines << endl;
	}
	else if (order.compare("-s") == 0)
	{
		//该操作在main函数里完成
	}
	else
		cout << "命令有错误,无法识别" << endl;
	recount();
}

  

主函数main的调用

#include "Filecountclass.h"
#include 
#include 

using namespace std;

int main()
{
    string Path = "D:\\vs代码\\fileline\\fileline";
    string testorder = "-a";
    string testfilename = "*.txt";
    string s[20] = { "" };
    string orders[5] = { "-c","-w","-l" ,"-a","-s"};        //总命令和
    string trueorder[5] = {};
    string orderline = "";

    Filecount testfile ;
    cout << "请输入要在哪里搜索文件(如D:\\vs代码\\fileline\\fileline)" << endl;
    getline(cin,Path);
    cout << "请输入要执行的指令行(可连续输入,重复输入同一个无效)" << endl;
    cout <<"-c返回字符数  -w返回单词数 -l返回行数 -a返回复杂的行数数据 -s查找符合条件的文件返回"<<endl;
    getline(cin, orderline);
    cout << "请输入要搜索的文件名(如 *.txt /test.txt)" << endl;
    getline(cin, testfilename);

    int p = 0;            //记录命令的真正个数
    for (int t = 0; t < 5; t++)
    {
        if (orderline.find(orders[t]) != string::npos)
        {
            trueorder[p] = orders[t];
            p++;
        }    
    }

    int i = 0;
    testfile.filesearch(Path, testfilename, s, i); //在Path里搜索符合的文件名
    if (i == 0)
        cout << "无符合的文件,输入错误!" << endl;
    if (p == 0)
        cout << "无效指令,无法执行!" << endl;
    for (int j = 0; j < i; j++)
    {
        cout << "文件" << s[j] << "的数据:" << endl;
        for (int k = 0; trueorder[k] != ""&& k< p ;k++)
        {
            testfile.filecount(s[j], trueorder[k]);
        }
    }
        //Filecount对象只有一个却对应多个文件
    system("pause");
    return 0;
}

 

测试运行

基础功能(文件名只能输一个)

个人项目wc.exe的功能实现(C++)_第1张图片

 

扩展功能

个人项目wc.exe的功能实现(C++)_第2张图片

 

项目小结

对github的使用很不熟悉,学会了sting的很多方法以及_finddata_t,在写代码之前要多加构思,不能想到什么就写什么,效率极低。这里面的函数每次都是写好了又改,搞得测试做了很多,但是没什么参考价值。按我的想法应该是一个文件对应一个Filecount的实例化对象,可以保存程序运行的结果,但是在一些地方的想法实现时让代码很难修改回这个模式,所以说还是对函数的不熟悉,导致面对问题时不能快速想到用什么函数,如何去做。思路的不清晰导致行为的不确定,这次的项目写了很久,算是对自己能力的一次测试吧。希望自己在今后的学习可以更进一层楼。

 

你可能感兴趣的:(个人项目wc.exe的功能实现(C++))