一.用户需求
程序处理用户需求的模式为:
- wc.exe [parameter][filename]
在[parameter]中,用户通过输入参数与程序交互,需实现的功能如下:
1、基本功能
- 支持 -c 统计文件字符数
- 支持 -w 统计文件单词数
- 支持 -l 统计文件总行数
2、拓展功能
- 支持 -a 返回高级选项(代码行 空行 注释行)
- 支持 -s 递归处理符合条件的文件
二.功能实现
为了增加程序的可读性,我对各项功能进行了模块化。共写了六个子函数。其中void charcount(FILE *fp)用于统计文件中字符的数量,void wordcount(FILE *fp)用于统计文件中单词的数量,void linecount(FILE *fp)用于统计文件的行数,void mixline(FILE *fp)实现统计文件中的代码行,注释行,空行的数量,void multi_file(char *path,char *func)用于处理文件目录下的多个文件void filesearch(char *path, int layer,char *func,char *q)递归处理目录下符合条件的文件。首先应当把用户输入的字符串读到数组string[100]中,然后对字符串进行处理,分离出功能选项和文件路径。然后分别存储在function[20]和file[100]中,使用for循环结构以及switch分支语句对用户要求的各项功能进行实现。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <io.h>
void charcount(FILE *fp); //统计文件中的字符数量
void wordcount(FILE *fp); //统计文件中的单词数量
void linecount(FILE *fp); //统计文件行数
void mixline(FILE *fp); //统计文件中的代码行,注释行,空行的数量
void multi_file(char *path,char *func); //处理文件目录下的多个文件
void filesearch(char *path, int layer,char *func,char *q); //递归处理目录下符合条件的文件
int main()
{
int i,j,flag=0;
FILE *fp;
char file[100],string[200],function[20];
while(1)
{
if(flag==1)
printf("\n");
printf("wc.exe ");
flag=1;
gets(string);
for(i=0,j=0;string[i]!='\0';i++)
{
if(string[i]=='-'&&islower(string[i+1])) //islower(char a)用于判断字符是否是字母
{
function[j]=string[i+1];
j++;
if(string[i+2]!=' ')
{
printf("Input is wrong!");
exit(0);
}
i+=2;
}
if(i!=0&&string[i+1]!='-')
{
i++;
break;
}
}
function[j]='\0';
j=0;
while(string[i]!='\0')
{
file[j]=string[i];
i++;
j++;
}
file[j]='\0';
if(function[0]!='s')
{
if((fp=fopen(file,"r"))==NULL) //读取文件内容
{
printf("The file is not found!\n");
exit(0);
}
}
for(i=0;function[i]!='\0';i++)
{
switch(function[i])
{
case 'c': charcount(fp); break;
case 'w': wordcount(fp); break;
case 'l': linecount(fp); break;
case 'a': mixline(fp); break;
case 's': multi_file(file,function); break;
default:
{
printf("Input is wrong!");
break;
}
}
if(function[0]=='s')
break;
}
}
return 0;
}
用户操作-c:void charcount(FILE *fp)可以统计打开的文件中的字符数(除掉“\n”),遇到换行符不会进行统计,feof(fp)用于判断文件指针是否指向了文件的末尾,到了文件末尾则跳出循环。在完成打印操作后,要使用rewind(fp)使文件指针指向文件的开头,以便于其它子函数对文件进行操作。
void charcount(FILE *fp)
{
char sign;
int charnum=0;
do{
sign=fgetc(fp);
if((feof(fp)))
break;
else if(sign=='\n'){}
else
charnum++;
}
while(1);
printf("Number of characters:%d\n",charnum);
rewind(fp); //进行完操作后使文件指针指向文件头
}
用户操作-w:void wordcount(FILE *fp)实现对单词数的统计(我所定义的单词是以字母或下划线开头的由字母,数字,下划线构成的符号串)。其中使用到了isalpha(char sign),isalnum(char sign),这两个函数均包含在了ctype.h的头文件中,分别用于识别字母,字母和数字。库函数应用可以减少代码量。
void wordcount(FILE *fp)
{
int i=0;
char sign;
do{
sign=fgetc(fp);
if((feof(fp)))
break;
if((unsigned char)isalpha(sign)||(int)sign==95)
{
while((unsigned char)isalnum(sign)||(int)sign==95)
sign=fgetc(fp);
i++;
}
}
while(1);
printf("Number of words:%d\n",i);
rewind(fp);
}
用户操作-l:void linecount(FILE *fp)可以统计文件中的行数。主要是通过判断换行符来计数。
void linecount(FILE *fp)
{
int line=0;
char sign;
sign=fgetc(fp);
if(sign!=NULL)
line=1;
do{
sign=fgetc(fp);
if((feof(fp))) //判断是否到了文件尾
break;
if(sign=='\n')
{
sign=fgetc(fp);
if(sign!=NULL&&sign!='\n')
line++;
}
}
while(1);
printf("Number of rows:%d\n",line);
rewind(fp);
}
用户操作-a:void mixline(FILE *FP)实现统计文件中的代码行,注释行,空行的数量。
空行:本行全部是空格或者格式控制字符,如果包括代码,则只有不超过一个可显示字符,例如“}”。
代码行:本行包括多于一个字符的代码。
注释行:本行不是代码行,并且本行包括注释,"}//注释”这种情况也属于注释行。
利用分支结构对各种情况进行判断,其中fseek(fp,-1,1)的作用是回退一个字符,这样改变文件指针的位置的方法即实现可不使用字符串保存字符来进行判断。
void mixline(FILE *fp)
{
int i=0,blankline=0,codeline=0,noteline=0;
char sign;
do{
if(feof(fp))
break;
sign=fgetc(fp);
if((feof(fp)))
break;
while(sign=='\t'||sign==' ')
sign=fgetc(fp);
if(sign=='\n')
{
sign=fgetc(fp);
i=1;
while(sign=='\t'||sign==' ')
sign=fgetc(fp);
if(sign=='\n')
{
blankline++;
i=0;
}
else if(sign=='}'||sign=='{')
{
sign=fgetc(fp);
while(sign=='\t'||sign==' ')
{
sign=fgetc(fp);
i++;
}
if(sign=='\n')
{
blankline++;
i=0;
}
else if((feof(fp)))
blankline++;
else
{
fseek(fp,-i,1);
i=0;
}
}
else
fseek(fp,-1,1); //fseek()函数可以对文件指针进行操作,使指针向前或向后
}
else if(sign=='}'||sign=='/')
{
sign=fgetc(fp);
while(sign==' '||sign=='\t')
sign=fgetc(fp);
if(sign=='/'||sign=='*')
{
noteline++;
while(sign!='\n')
{
sign=fgetc(fp);
if(feof(fp))
break;
}
if(!(feof(fp)))
fseek(fp,-1,1);
}
}
else
{
codeline++;
while(sign!='\n')
sign=fgetc(fp);
fseek(fp,-1,1);
}
}
while(1);
printf("Number of blank lines: %d\n",blankline);
printf("Number of code lines: %d\n",codeline);
printf("Number of comment lines: %d\n",noteline);
rewind(fp);
}
用户操作-s:void multi_file(char *path,char *func)用于把文件路径和文件的后缀名分离,分别保存在两个数组中。然后调用void filesearch(char *path, int layer,char *func,char *q),实现对文件的递归处理。void filesearch(char *path, int layer,char *func,char *q)中的结构体_finddata_t,包含在io.h头文件中。
struct _finddata_t
{
unsigned attrib; //文件属性
time_t time_create; //创建时间
time_t time_access; //文件最后一次被访问时间按
time_t time_write; //文件最后一次被修改时间
_fsize_t size; //文件大小
char name[_MAX_FNAME]; //文件名
};
unsigned atrrib:文 件属性的存储位置。它存储一个unsigned单元,用于表示文件的属性。文件属性是用位表示的,主要有以下一些:_A_ARCH(存档)、 _A_HIDDEN(隐藏)、_A_NORMAL(正常)、_A_RDONLY(只读)、_A_SUBDIR(文件夹)、_A_SYSTEM(系统)。_findfirst(curr, &filefind))用于搜索与指定的文件名称匹配的第一个实例,若成功则返回第一个实例的句柄,否则返回-1L,_findnext(handle, &filefind)搜索与_findfirst函数提供的文件名称匹配的下一个实例,若成功则返回0,否则返回-1。递归调用可以实现若目录下存在文件夹,则可以进入文件夹继续搜索相应后缀名的文件。
void multi_file(char *path,char *func)
{
int i,j=0;
char p[100],q[10];
for(i=0;path[i+1]!='*';i++)
{
if(path[i]=='\0')
{
printf("Input is wrong!");
exit(0);
}
p[i]=path[i];
}
p[i]='\0';
for(i=i+1;path[i]!='\0';i++,j++)
q[j]=path[i];
q[j]='\0';
filesearch(p, 0,func,q);
}
void filesearch(char *path, int layer,char *func,char *q)
{
struct _finddata_t filefind;
char curr[100],path1[100];
int done = 0,handle,i,flag=0,j;
FILE *fp;
strcpy(path1,path);
strcpy(curr,path1);
strcat(curr,"\\");
strcat(curr,q);
if((handle = _findfirst(curr, &filefind)) != -1)
{
if(handle>0)
done=0;
else
done=-1;
while(!done)
{
if(!(strcmp(filefind.name,".")))
done = _findnext(handle, &filefind);
if(flag==1)
done = _findnext(handle, &filefind);
if(done==-1)
break;
if(strcmp(filefind.name, "..") == 0)
{
flag=1;
continue;
}
if((_A_SUBDIR == filefind.attrib)) // 判断是否是文件夹
{
flag=1;
strcat(path1,"\\");
strcat(path1,filefind.name);
filesearch(path1, layer+1,func,q); // 递归遍历子目录
strcpy(path1,path);
}
else
{
flag=1;
strcat(path1,"\\");
strcat(path1,filefind.name);
if((fp=fopen(path1,"r"))==NULL) //读取文件内容
{
printf("The file is not found!\n");
exit(0);
}
printf("The file path:%s\n",path1);
strcpy(path1,path);
for(i=0;func[i]!='\0';i++)
{
if(func[i]=='s')
continue;
switch(func[i])
{
case 'c': charcount(fp); break;
case 'w': wordcount(fp); break;
case 'l': linecount(fp); break;
case 'a': mixline(fp); break;
default:
{
printf("Input is wrong!");
break;
}
}
}
}
}
_findclose(handle);
}
}
三. 运行结果
1、基本功能支持
-c 统计文件字符数支持
-w 统计文件单词数支持
-l 统计文件总行数
实现对本地文件的操作
2、拓展功能支持
-a 返回高级选项(代码行 空行 注释行)支持
-s 递归处理符合条件的文件
文件路径如下图:E:\a
E:\a\文件
实现对E:\a中的所有类型文件以及子文件夹中的文件操作
实现对E:\a中后缀名为.cpp的文件的操作
至此,wc.exe的基本功能以及扩展功能就已经实现了,在编程的过程中确实学习到了很多知识,网络中有资源宝库,能得到自己需要的东西。可能程序还有地方不完善,希望老师以及同学们多多指教。