WorkCount

此项目码云链接

软件测试方法和技术第二次作业

1 作业内容

根据WordCount的需求描述,先编程实现,再编写单元测试,最后撰写博客。至少完成需求中的基本功能。

2 WordCount需求说明

WordCount的需求可以概括为:对程序设计语言源文件统计字符数、单词数、行数,统计结果以指定格式输出到默认文件中,以及其他扩展功能,并能够快速地处理多个文件。

2.1 基础功能

wc.exe -c file.c //返回文件 file.c 的字符数
wc.exe -w file.c //返回文件 file.c 的单词总数
wc.exe -l file.c //返回文件 file.c 的总行数
wc.exe -o outputFile.txt //将结果输出到指定文件outputFile.txt
注意:
空格,水平制表符,换行符,均算字符。
由空格或逗号分割开的都视为单词,且不做单词的有效性校验,例如:thi#,that视为用逗号隔开的2个单词。
-c, -w, -l参数可以共用同一个输入文件,形如:wc.exe –w –c file.c 。
-o 必须与文件名同时使用,且输出文件必须紧跟在-o参数后面,不允许单独使用-o参数。

2.2 扩展功能

wc.exe -s //递归处理目录下符合条件的文件
wc.exe -a file.c //返回更复杂的数据(代码行 / 空行 / 注释行)
wc.exe -e stopList.txt // 停用词表,统计文件单词总数时,不统计该表中的单词
[file_name]: 文件或目录名,可以处理一般通配符。
其中,
代码行:本行包括多于一个字符的代码。
空 行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。
注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释:
}//注释
在这种情况下,这一行属于注释行。
-e 必须与停用词文件名同时使用,且停用词文件必须紧跟在-e参数后面,不允许单独使用-e参数。
stopList.txt中停用词可以多于1个,单词之间以空格分割,不区分大小写,形如:
while if switch
则while,if,switch作为3个停用词,在单词统计的时候不予考虑。停用词表仅对单词统计产生影响,不影响字符和行数的统计。

2.3 高级功能

wc.exe -x //该参数单独使用,如果命令行有该参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、单词数、行数等全部统计信息。

3 解题过程

解题思路

本作业总体而言比较简单,因此采用面向过程方法就可轻松完成。首先我们知道,Main函数的args[]参数用于储存输入参数。那么命令中的**-o**,**-w**,**-c**等参数的检测就可以交给它来解决。这里为了方便,我们约定- w|c|l|a,-o的输入顺序。也就是说,先指明要操作的文件及命令再指明输出文件

step1 对输入进行检测

出于程序的健壮性考虑,首先要实现的功能应是输入检测。检测内容主要有以下几点 **参数名是否正确** **参数是否重复输入** **参数输入顺序是否正确** **文件名是否符合规范** **若符合规范,文件是否存在** 参数检测 ``` for (i = 0; i < args.Length; i++) { //fchar等是初始值为0的int型标志符,每个参数对应一个 if (args[i] == "-c") { fChar++; } else if (args[i] == "-w") { fWord++; } else if (args[i] == "-o") { fOutput++; } else if (args[i] == "-l") { fLine++; } else if(args[i] == "-a") { fMore++; } else if(args[i] == "-s") { fRecursion++; } } ```

对于-o参数除了更改标识符外还要进行赋值操作,其中fileName和ResultFile均为全局变量

                if (args[i] == "-o")
                {
                    if (fChar == 0 && fWord == 0 && fLine == 0)
                    {   //标识符为0说明在-o参数前面并没有- c|w|l|a 参数
                        Console.WriteLine("错误的命令,若使用-o参数请先在前面使用-c或-w或-i或-l指令");
                        return false;
                    }
                    fileName = args[i - 1];     //-o参数前一个参数必定为操作文件
                    ResultFile = args[i + 1];   //-o参数后一个参数必定为储存文件
                }

参数重复检测

            if (fChar > 1 || fWord > 1 || fOutput > 1 || fLine > 1 || fMore > 1)
            {   //如果大于一表示输入了至少两个重复参数
                Console.WriteLine("输入的参数不能重复,请修改后再输入");
                return false;
            }

文件规范检测

            if (!Regex.IsMatch(fileName, "\\w*\\.(txt|c|docx)"))
            {   //检查操作文件格式,利用正则表达式,存储文件的检测同理,将fileName更改为ResulFile即可
                Console.WriteLine("Wrong filename,please try again!");
                Console.WriteLine("Allowed filename:*.(txt|c|docx)");
                return false;
            }

文件是否存在

            if(!File.Exists(fileName))
            {    //不存在该文件
                if (!Regex.IsMatch(fileName, "\\*\\.(txt|c|docx)"))
                {    //如果文件名格式不为*.c|txt|docx
                    Console.WriteLine("文件不存在!请重新输入");
                    return false;
                }
            }
  • 基础功能的实现
    对于-c,-w,-l,-a实现的基本思路相同。读取文件内容——>对内容进行处理——>保存需要记录的数据——>关闭文件流
    上述四个参数可分别作为四个方法,WordsCount(),CharCount(),LineCount(),MoreCount().其中对于文件的处理均采用StreamReader,保存到字符串Str中
StreamReader sr = new StreamReader(fileName);    //打开文件
string str = sr.ReadToEnd();                                      //读取并保存到str字符串中

四者字符串处理方法不同,字符计数可新建Char[]变量,再将读出的文件内容赋值,char数组的长度即为字符数.

char[] output = str.ToCharArray();

单词计数则利用字符串的split()方法,将读出的内容用','和‘ ’隔开再赋值给string[],string数组的长度即为单词数

string[] Words = str.Split(' ', ',');

行数与单词计数同理,也是利用Split()方法,只是分隔符变成了'\n',string数组的长度即为行数

string[] Lines = str.Split('\n');

代码行/注释行/空行,由于是对每行进行判断,先用string.Split('\n')将文件内容赋值给string数组More[],然后对每一行More[i]进行判断。具体代码如下

  for(i = 0;i < More.Length;i++)
            {
                chr = More[i].Replace(" ", "").Replace("\t","").ToCharArray();   //去掉空格和制表符
                if(chr.Length > 1)
                {   //字符数大于一,可能是代码行,可能是注释行
                    if(chr[0] == '}' || chr[0] == '{' && chr[1] == '/' && chr[2] == '/')
                    {   //去掉空格后如果包括{或}加上//则为注释行
                        aNote++;
                    }
                    else
                    {   //否则是代码行
                        aCode++;
                    }                       
                }
                else 
                {   //去掉空行后字符数小于一,则是空行
                    aEmpty++;
                }
            }

其中aNote、aCode、aEmpty为int型计数变量,其他参数也每个对应一个

- 参数-s的实现
首先获取当前目录及其子目录下所有的文件信息,然后找出符合文件名要求的文件进行对应处理,代码如下
        /// <summary>
        /// 用于递归处理目录下符合条件的文件
        /// </summary>
        static void Recursion()
        {
            List<FileInfo> file = GetAllFilesInDirectory(".\\");    //此方法为自定义方法,返回值为FileInfo类型的列表
            string[] suffix = fileName.Split('.');  //文件后缀
            string pattern = fileName.Replace("*.", "\\w*\\.").Replace(suffix[1], "") + "(" + suffix[1] + ")$";     //将文件名在正则式正确表达出来
            foreach(var f in file)
            {
                    if (Regex.IsMatch(f.Name, pattern))
                {          //如果文件名符合正则表达式      
                    fileName = f.Name;
                    //根据参数决定该调用哪些方法
                    if (fChar > 0)
                        CharCount();    //字符计数
                    if (fWord > 0)
                        WordsCount();   //单词计数
                    if (fLine > 0)
                        LinesCount();   //行数计数
                    if (fMore > 0)
                        MoreCount();    //代码行、空行、注释行计数

                    Print();    //将结果输出到屏幕,print为自定义方法
                } 
            }
        }

由于题目要求无论参数顺序输出顺序相同,故将输出作为一个方法Print()

 static void Print()
        {
            if (fChar > 0)
                Console.WriteLine(fileName + ",字符数:" + aChar.ToString());
            if(fWord > 0)
                Console.WriteLine(fileName + ",单词数:" + aWord.ToString());
            if(fLine > 0)
                Console.WriteLine(fileName + ",行数:" + aLine.ToString());
            if (fMore > 0)
            {
                string print = string.Format("代码行/空行/注释行:{0}/{1}/{2}", aCode, aEmpty, aNote);
                Console.WriteLine(print);
            }

在主界面调用方法即可

  static void Main(string[] args)
        {
            //检查参数,此外-o参数在此方法已解决
            if(!Check(args))   
            {
                return;
            }

            //根据参数决定该调用哪些方法
            if (fRecursion > 0)
            {
                Recursion();    //循环
                return;
            }
            if (fChar > 0)
                CharCount();    //字符计数
            if (fWord > 0)
                WordsCount();   //单词计数
            if (fLine > 0)
                LinesCount();   //行数计数
            if (fMore > 0)
                MoreCount();    //代码行、空行、注释行计数


            Print();    //将结果输出到屏幕
        }

测试

测试文件

测试数据



总结

通过这次作业,我对于文件的操作掌握得更加好,并且对于软件测试也有了更深的了解

posted @ 2018-09-26 20:42  北冥虾  阅读(337)  评论(0编辑  收藏  举报