个人项目:Word Count

GitHub地址:https://github.com/Guyuefangzheng/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 返回更复杂的数据 (实现)

2、psp表

*PSP2.1* *Personal Software Process Stages* *预估耗时(分钟)* *实际耗时(分钟)*
Planning 计划 60 30
·Estimate · 估计这个任务需要多少时间 60 30
Development 开发 1220 870
· Analysis · 需求分析 (包括学习新技术) 150 180
· Design Spec · 生成设计文档 40 20
·Design Review · 设计复审 (和同事审核设计文档) 40 20
·Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 20
· Design · 具体设计 60 60
· Coding · 具体编码 400 360
· Code Review · 代码复审 100 60
· Test · 测试(自我测试,修改代码,提交修改) 200 150
Reporting 报告 120 90
· Test Report · 测试报告 60 30
·Size Measurement · 计算工作量 30 30
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 30
合计 1200 990

3、解题思路

​ 一开始看到题时想的是用c来实现还是用Java,最后还是选择了熟悉的c。这次的题目涉及到的知识点命令行参数、文件操作这两部分以前没有去深究,因为书都在学校,所以面向搜索引擎来学习这部分的内容。

4、设计实现过程

​ 每一个功能一个函数,再加上一个main函数一共六个函数,在这几个功能模块中WordCount()调用CharCount()来判断该文件是否为空,SearchFile()调用三个基础功能的函数来实现批量操作。

5、代码说明

头文件

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<io.h>
#include<time.h>

基本功能:获取字符数

int CharCount(char *file){	//字符数统计 
	FILE *fp = NULL;
	fp = fopen(file,"r");
	if(fp==NULL){
		printf("没有找到此文件!
");
		exit(-1);
	}
	
	int Ccount = 0;
	char temp;
	while(!feof(fp)){	//文件结束判断 
		temp = fgetc(fp);
		if(temp!=' '&&temp!='
'&&temp!='	'&&temp!=EOF)
			Ccount++;
	} 
	fclose(fp);
	return Ccount; 
}

首先判断该文件是否存在,不存在就退出,不是一个有效字符有四种情况:空格、换行、缩进格、txt文件结束符。

.
基本功能:获取词数

int WordCount(char *file){	//词数统计 
	if(CharCount(file)==0)
		return 0;
		
	int Wcount = 0;
	bool b = 0;	//用来标记是否是字符 
	char temp;
	FILE *fp = NULL;
	fp = fopen(file,"r");
	while(!feof(fp)){
		temp = fgetc(fp);
		if(temp==' '||temp=='	'||temp=='
'||temp==EOF){	//四种情况,包括文件结束 
			if(b==1){	 //前面是字符,词数+1 
				Wcount++;
				b = 0;
			}
		} 
		else
			b = 1;	//如果是字符,b标记为1 
	}
	fclose(fp);
	return Wcount;
}

通过调用字符统计函数来判断该文件是否存在和是否为空,用一个bool变量来标记,当bool值为1时词数加一。

.
基本功能:获取行数

int LineCount(char *file){	//行数统计 
	FILE *fp = NULL;
	fp = fopen(file,"r");
	if(fp==NULL){
		printf("没有找到此文件!
");
		exit(-1);
	}
	
	int Lcount = 1;
	char temp;
	while(!feof(fp)){
		temp = fgetc(fp);
		if(temp=='
')
			Lcount++;
	}
	fclose(fp);
	return Lcount;
}

行数初始为1,遇到 加1。

.
扩展功能:处理目录下符合条件的文件

int SearchFile(){	//处理目录下符合条件的文件
    struct _finddata_t fileinfo;	//文件存储信息结构体 
    long fHandle;	//保存文件句柄
    int t=0;	//文件数记录器
    fHandle = _findfirst("*.txt",&fileinfo);	//查找当前目录下的txt文件
	 
    if(fHandle==-1L)
    	printf( "当前目录下没有txt文件
");
    else
    	do{
    		t++; 
    		printf("找到文件:%s
",fileinfo.name);
    		printf("字符数为:%d
",CharCount(fileinfo.name));
    		printf("词数为:%d
",WordCount(fileinfo.name));
    		printf("行数为:%d

",LineCount(fileinfo.name));
		}while(_findnext(fHandle,&fileinfo)==0);
		_findclose(fHandle);	//关闭文件链接
		return t; 
}

通过头文件io.h中的文件结构体 _finddata_t ,使用 _findfist 和 _findnext 两个函数来查找文件,找到文件,调用基础模块。

.
扩展功能:返回更复杂的数据(代码行 / 空行 / 注释行)

int MoreLine(char *file){	//返回更复杂的数据(代码行 / 空行 / 注释行)
	FILE *fp = NULL;
	fp = fopen(file,"r");
	if(fp==NULL){
		printf("没有找到此文件!
");
		exit(-1);
	}
	
	int nNum = 0,cNum = 0,aNum = 0;
	bool b = 0;	// 
	int c;
	char temp;
	while(!feof(fp)){
		temp = fgetc(fp);
		if(temp=='
'||temp==EOF){	//换行或者文件结束 
		//在换行或者文件结束时判断出类型 
			if(c==0)	//空行 
				nNum++;
			else if(c==1){	//代码行 
				cNum++;
				c = 0;
			}
			else{	//注释行 
				aNum++;
				c = 0;
			}
		}
		else if(temp!=' '&&temp!='	'&&temp!='{'&&temp!='}'){
			//判断出代码行和注释行,剩下的即是空行 
			if(temp=='/'){
				if(c==0&&b==0)
					b = 1;
				else if(c==0&&b==1){
					c = 2;
					b = 0;
				}
			} 
			else if(c!=2)
				c = 1;	 
		}
	}
	fclose(fp);
	printf("空行数为:%d
",nNum);
	printf("代码行数为:%d
",cNum);
	printf("注释行数为:%d
",aNum);
	return nNum+cNum+aNum;
}

//设置两个标记变量,一个整形变量用0,1,2来标记该行是空行、代码行还是注释行,一个bool变量用来判断注释行。标记所有的代码行和注释行,剩下的就都是空行了。

.
main函数

int main(int argc,char *argv[]){
	clock_t start_t,finish_t;
	double Total_time;
	start_t = clock();
	if(!strcmp(argv[1],"-c"))
		printf("charnumber: %d
",CharCount(argv[2]));
	else if(!strcmp(argv[1],"-w"))
		printf("wordnumber: %d
",WordCount(argv[2]));
	else if(!strcmp(argv[1],"-l"))
		printf("linenumber: %d
",LineCount(argv[2]));
	else if(!strcmp(argv[1],"-s"))
		printf("find txt number: %d
",SearchFile());
	else if(!strcmp(argv[1],"-a"))
		MoreLine(argv[2]);
	else{ 
		printf("请输入正确的指令!
");
		exit(1);
	}
	finish_t = clock();
	Total_time = (double)(finish_t - start_t) / CLOCKS_PER_SEC;//将时间转换为秒
	printf("%f seconds
",Total_time);
}

用头文件time.h中的 clock_t 函数来计算时间。

6、测试运行

个人项目:Word Count

个人项目:Word Count

个人项目:Word Count

个人项目:Word Count

7、项目小结

1、这次项目暴露出了我其实对c语言还只是处于最基础的了解,文件操作、时间函数都不熟悉,都是现找现做的,需要加深学习
2、没能对项目的完成时间有个正确的预估,对如何估算PSP的时间还需学习
3、查资料的时候注意到一点,feof(FILE *fp)如果文件结束,则返回非0值,否则返回0,但是读完最后一个字符后,fp->flag仍然没有被设置为_IOEOF,因为还有个文件结束符,直到再次调用fgetc(),feof()才能探测到文件结尾。并且不应用EOF来代替feof(),因为fgetc()返回-1时有两种情况:读到文件结尾或是读取错误。