一.用户需求

程序处理用户需求的模式为:

  • 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=;
FILE *fp;
char file[],string[],function[];
while()
{
if(flag==)
printf("\n");
printf("wc.exe ");
flag=;
gets(string);
for(i=,j=;string[i]!='\0';i++)
{
if(string[i]=='-'&&islower(string[i+])) //islower(char a)用于判断字符是否是字母
{
function[j]=string[i+];
j++;
if(string[i+]!=' ')
{
printf("Input is wrong!");
exit();
}
i+=;
}
if(i!=&&string[i+]!='-')
{
i++;
break;
}
}
function[j]='\0';
j=;
while(string[i]!='\0')
{
file[j]=string[i];
i++;
j++;
}
file[j]='\0';
if(function[]!='s')
{
if((fp=fopen(file,"r"))==NULL) //读取文件内容
{
printf("The file is not found!\n");
exit();
}
} for(i=;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[]=='s')
break;
}
}
return ;
}

  用户操作-c:void charcount(FILE *fp)可以统计打开的文件中的字符数(除掉“\n”),遇到换行符不会进行统计,feof(fp)用于判断文件指针是否指向了文件的末尾,到了文件末尾则跳出循环。在完成打印操作后,要使用rewind(fp)使文件指针指向文件的开头,以便于其它子函数对文件进行操作。

void charcount(FILE *fp)
{
char sign;
int charnum=;
do{
sign=fgetc(fp);
if((feof(fp)))
break;
else if(sign=='\n'){}
else
charnum++;
}
while();
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=;
char sign;
do{
sign=fgetc(fp);
if((feof(fp)))
break;
if((unsigned char)isalpha(sign)||(int)sign==)
{
while((unsigned char)isalnum(sign)||(int)sign==)
sign=fgetc(fp);
i++;
}
}
while();
printf("Number of words:%d\n",i);
rewind(fp);
}

  用户操作-l:void linecount(FILE *fp)可以统计文件中的行数。主要是通过判断换行符来计数。

void linecount(FILE *fp)
{
int line=;
char sign;
sign=fgetc(fp);
if(sign!=NULL)
line=;
do{
sign=fgetc(fp);
if((feof(fp))) //判断是否到了文件尾
break;
if(sign=='\n')
{
sign=fgetc(fp);
if(sign!=NULL&&sign!='\n')
line++;
}
}
while();
printf("Number of rows:%d\n",line);
rewind(fp);
}

  用户操作-a:void mixline(FILE *FP)实现统计文件中的代码行,注释行,空行的数量。

  空行:本行全部是空格或者格式控制字符,如果包括代码,则只有不超过一个可显示字符,例如“}”。

  代码行:本行包括多于一个字符的代码。

  注释行:本行不是代码行,并且本行包括注释,"}//注释”这种情况也属于注释行。

  利用分支结构对各种情况进行判断,其中fseek(fp,-1,1)的作用是回退一个字符,这样改变文件指针的位置的方法即实现可不使用字符串保存字符来进行判断。

void mixline(FILE *fp)
{
int i=,blankline=,codeline=,noteline=;
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=;
while(sign=='\t'||sign==' ')
sign=fgetc(fp);
if(sign=='\n')
{
blankline++;
i=;
}
else if(sign=='}'||sign=='{')
{
sign=fgetc(fp);
while(sign=='\t'||sign==' ')
{
sign=fgetc(fp);
i++;
}
if(sign=='\n')
{
blankline++;
i=;
}
else if((feof(fp)))
blankline++;
else
{
fseek(fp,-i,);
i=;
}
}
else
fseek(fp,-,); //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,-,);
}
}
else
{
codeline++;
while(sign!='\n')
sign=fgetc(fp);
fseek(fp,-,);
} }
while();
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=;
char p[],q[];
for(i=;path[i+]!='*';i++)
{
if(path[i]=='\0')
{
printf("Input is wrong!");
exit();
}
p[i]=path[i];
}
p[i]='\0';
for(i=i+;path[i]!='\0';i++,j++)
q[j]=path[i];
q[j]='\0';
filesearch(p, ,func,q);
}

void filesearch(char *path, int layer,char *func,char *q)
{
struct _finddata_t filefind;
char curr[],path1[];
int done = ,handle,i,flag=,j;
FILE *fp;
strcpy(path1,path);
strcpy(curr,path1);
strcat(curr,"\\");
strcat(curr,q);
if((handle = _findfirst(curr, &filefind)) != -)
{
if(handle>)
done=;
else
done=-;
while(!done)
{
if(!(strcmp(filefind.name,".")))
done = _findnext(handle, &filefind);
if(flag==)
done = _findnext(handle, &filefind);
if(done==-)
break;
if(strcmp(filefind.name, "..") == )
{
flag=;
continue;
}
if((_A_SUBDIR == filefind.attrib)) // 判断是否是文件夹
{
flag=;
strcat(path1,"\\");
strcat(path1,filefind.name);
filesearch(path1, layer+,func,q); // 递归遍历子目录
strcpy(path1,path);
}
else
{
flag=;
strcat(path1,"\\");
strcat(path1,filefind.name);
if((fp=fopen(path1,"r"))==NULL) //读取文件内容
{
printf("The file is not found!\n");
exit();
}
printf("The file path:%s\n",path1);
strcpy(path1,path);
for(i=;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的基本功能以及扩展功能就已经实现了,在编程的过程中确实学习到了很多知识,网络中有资源宝库,能得到自己需要的东西。可能程序还有地方不完善,希望老师以及同学们多多指教。

(第三周)wc.exe—命令行实现对指定目录下文件的操作的更多相关文章

  1. 如何用DOS命令,获取一个目录下的文件数目

    发信人: GOOGOODALLS (我爱Figo), 信区: DOS 标  题: 如何用DOS命令,获取一个目录下的文件数目? 发信站: 水木社区 (Fri Mar  9 08:40:01 2007) ...

  2. DOS命令行(1)——Windows目录与文件应用操作

    cd 1.使用cd快速切换到指定盘符与目录中 命令格式1:cd [/d] [<盘符>][<路径>] 或 chdir [/d] [<盘符>][<路径>] ...

  3. Linux常用基础命令整理:关机命令、查看目录下文件命令等

    Linux常用基础命令整理:关机命令.查看目录下文件命令等 整理了一些Linux常用基础命令,欢迎指正. 首先记住四个热键,学会这四个键,收益一辈子. Tab按键---命令补齐功能Ctrl+c按键-- ...

  4. 查看 /var/log目录下文件个数 命令tree 、cut

    查看 /var/log目录下文件个数 方法1. [root@oldboy learn_shell]# tree -L 1 /var/log/ |tail -1 5 directories, 42 fi ...

  5. find命令查找包含指定内容的文件

    find / | xargs grep function 查找系统根目录下面的所有文件的内容中包含有function字符串的文件列表. find .|xargs grep xfind . -exec ...

  6. 用bash命令得到Windows一个目录下的所有文件并且把结果输入到一个文件

    方式一: 只用如下一条语句就可以了: tree/f>index.txt 放入一个文件中命名为"****.bat" 双击就会在该目录下生成一个index.txt文件,在这个文件 ...

  7. 命令行保存指定目录文件的名字(可包含文件夹文字)到txt文本文件

    Microsoft Visual Studio中配置OpenCV解决方案属性的时候, 需要将OpenCV的lib扩展名的库文件添加到属性的依赖列表里面,网上的有些人博客里面直接给出的会有问题(但大多数 ...

  8. linux 统计命令执行后的行数或者统计目录下文件数目

    ls |wc 是统计你这个目录下的文件数目.ls |wc -l是输出第一个结果即31即文件的数目.

  9. 运行cmd直接进入指定目录下的命令

    新建一个.bat批处理文件,文件命令为@ECHO OFF cmd /k cd /d c:data 运行该批处理文件cmd就可进入指定的文件夹,感兴趣的朋友可以参考下啊 新建一个.bat批处理文件,文件 ...

随机推荐

  1. Oracle Spatial中SDO_Geometry说明

    Oracle Spatial中SDO_Geometry说明 在ArcGIS中通过SDE存储空间数据到Oracle中有多种存储方式,分别有:二进制Long Raw .ESRI的ST_Geometry以及 ...

  2. Redis系列九:redis集群高可用

    Redis集群的概念: RedisCluster是redis的分布式解决方案,在3.0版本后推出的方案,有效地解决了Redis分布式的需求,当一个服务挂了可以快速的切换到另外一个服务,当遇到单机内存. ...

  3. BZOJ3105:[CQOI2013]新Nim游戏(线性基,贪心)

    Description 传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同).两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴.可以只拿一根,也可以拿走整堆火柴 ...

  4. Excel操作

    区间范围计算 方法一:用IF函数 方法二:构建一个辅助区域,用VLOOKUP函数 方法一:用IF函数 在F3中输入:=IF(E3>=90%,5%,IF(E3>=80%,4%,IF(E3&g ...

  5. [转]详解C#组件开发的来龙去脉

    C#组件开发首先要了解组件的功能,以及组件为什么会存在.在Visual Studio .NET环境下,将会有新形式的C#组件开发. 组件的功能 微软即将发布的 Visual Studio .NET 将 ...

  6. windows/Linux动态加载链接库问题

    windows: LoadLibraryA 指定的可执行模块映射到调用进程的地址空间并返回该 DLL 的句柄 HMODULE LoadLibraryA( LPCTSTR lpLibFileName// ...

  7. Android放大镜效果的简单实现

    package com.example.myapi.pictobig; import com.example.myapi.R; import android.content.Context; impo ...

  8. 【LeetCode92】Reverse Linked List II★★

    题目描述: 解题思路: 题目大意:给定一个链表,反转第m到第n个结点部分,m.n满足1 ≤ m ≤ n ≤ length of list. 解题思路参照LeetCode206题,用迭代法,不过要注意以 ...

  9. 20155218 Exp1 PC平台逆向破解(5)M

    20155218 Exp1 PC平台逆向破解(5)M 1. 掌握NOP.JNE.JE.JMP.CMP汇编指令的机器码 NOP:NOP指令即"空指令".执行到NOP指令时,CPU什么 ...

  10. EZ 2018 04 21 NOIP2018 模拟赛(十) -LoliconAutomaton的退役赛

    难得的一次Unrated,避免了重回1500的尴尬 其实题目都还可以,但只不过所有人T1都炸了,可能是数据的锅(假的) 而且我因为T1SB的把T2弃了,没想到是千年水题 T3莫名爆炸,然后TM的40分 ...