C&Pointer求解wc问题

一.Github项目地址:https://github.com/changjiang666/wordCount

 

 

二.PSP表格

WC问题统计C源文件中的字符,单词,行数,输出到默认文件或者指定文件中。拓展功能是实现多文件处理,文本行分类,忽略特定单词。根据需求,PSP表格如下:

 

PSP2.1

PSP阶段

预估耗时

(分钟)

实际耗时

(分钟)

Planning

计划

5 10

· Estimate

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

5 10

Development

开发

515 860

· Analysis

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

20 30

· Design Spec

· 生成设计文档

10 20 

· Design Review

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

5 10

· Coding Standard

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

20  30

· Design

· 具体设计

10 10 

· Coding

· 具体编码

400  600 

· Code Review

· 代码复审

20  100 

· Test

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

30  60 

Reporting

报告

100  100

· Test Report

· 测试报告

20 20 

· Size Measurement

· 计算工作量

20  30

· Postmortem & Process Improvement Plan

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

20   10
 

合计

620   970

 

 

 

 

三.解题思路

WC问题在UNIX系统的内核得以实现,骨干代码在黑皮C里面看到过,大概20行左右。刚拿到题目就没怎么在意,后来发现拓展功能也比较麻烦。整个题目的难点在于字符串的处理,文件操作不麻烦,只需要读操作就行,至于写操作只需要输出重定行即可。另一个难点在于递归处理当前路径下来的所有文件,刚开始想直接利用C进行系统调用,借助操作系统来完成这个功能(后来发现系统自动解析,白写了比较麻烦的系统调用),然而没法递归处理,GG。至于找资料,主要就是看了一下黑皮C最后面的库函数,需要用的时候在MSDN上面直接查。

1.UNIX系统中wc程序的骨干代码

 #include <stdio.h>

  #define IN 1

  #define OUT 0

  

  int main(int argc, char const *argv[])

  {

      int c, nl, nw, nc, state;

      state = OUT;

      nl = nw = nc = 0;

      while ((c = getchar()) != EOF) {

          ++nc;       

   if (c == '\n')

           ++nl;

      if (c == ' ' || c == '\n' || c == '\t')

            state = OUT;

      else if (state == OUT) {

             state = IN;

             ++nw;

         }

     }

     printf("%d %d %d\n", nl, nw, nc);

     return 0;

 }

基本功能实现就是抄的这个代码,只是把字符来源改成了缓存。

 

2. C的文件读操作

static int

filelength(FILE *fp);

char *

readfile(char *path);

这个也是网上的,基本照抄。

 

3. 输出重定向就是一行代码,利用了redirection

 

 

四.程序设计实现过程

根据功能划分,写了3个源文件。

file.c主要是文件操作,wc.c是整个程序的核心部分,help.c是给用户提供帮助的,用户输入-help可以查看帮助和自动打开作业网页。

 

1.file,h

Readfile把文件读到缓存,processBuf从缓存中提取出停用词表,返回指向这些字符的指针的指针。为了防止程序恶意修改缓存,将其设置为const。

getDir是递归处理所有的文件,没有用。

 

2. wc.h

先定义了一个struct,存储统计的基本信息,其实不用也行,用了只是为了方便,减少自己编程时需要记忆的内容。

CWL是统计文本信息的,利用了CPU运行程序的思想,把所有的东西都统计出来,需要什么就输出什么,缺点是增加了运行时间,不过只是增加了常系数。

在wc.c定义了一个外部属性限制在本文件范围的函数。

 

3.help.h

只有一个Help方法,输出帮助信息。

 

这几个函数基本没有关系,分开设计就是为了高内聚低耦合,全部的调用由main函数执行。

 

五.代码说明

介绍2个比较麻烦的部分:

 

1. main函数对命令行参数的解析

#if !DEBUG

    if(1 == argc)

    {

        printf("Please Open In CMD!\n\n\n\nAt CMD, Enter 'wc.exe -help' For More Help!\n\n\n");

        system("pause");

        return EXIT_FAILURE;

    }

    #endif

    for(i = 1; i < argc; ++i)

    {

        p = argv[i];

 

        /*capture -s*/

        if(strcmp(p, "-s") == 0)

            s_state = TRUE;

 

        /*capture -a*/

        if(strcmp(p, "-a") == 0)

            a_state = TRUE;

 

        if(strcmp(p, "-c") == 0)

            c_state = TRUE;

        if(strcmp(p, "-w") == 0)

            w_state = TRUE;

        if(strcmp(p, "-l") == 0)

            l_state = TRUE;

        if(strcmp(p, "-help") ==0)

        {

            help();

            return EXIT_SUCCESS;

        }

 

 

        /*loop until p point to '\0'*/

        while(*++p != NULL)

            ;

 

        /*p go back 2 char and compare to ".c"

          in order to identify the path name*/

        if(strcmp(p - 2, ".c") == 0)

            path[j++] = argv[i];

 

 

        /*p go back 4 char and compare to ".txt", at the same time argv[i - 1]

        stores "-e", so we can identify the word_list path and check user valid input*/

        if(strcmp(p - 4, ".txt") == 0 && strcmp(argv[i - 1],"-e") == 0 )

        {

            word_list_path = argv[i];

        }

 

        /*p go back 4 char and compare to ".txt", at the same time argv[i + 1]

        stores null pointer, so we can identify the file name*/

        if(strcmp(p - 4, ".txt") == 0 && argv[i + 1] == '\0')

        {

            /*if lack of "-o" before file name, throw error information*/

            if(strcmp(argv[i - 1], "-o") == 0)

                filename = argv[i];

            else

            {

                if(strcmp(argv[i - 1], "-e") != 0)

                {

                    printf("command line error.\n");

                    return EXIT_FAILURE;

                }

 

            }

        }

}

利用指针处理读取到的字符,由于要处理可能的各种输入,所以有些if嵌套比较深。到了最后感觉利用开关语句可以好一些,不想修改了,时间不够。在处理读取的每个命令行参数时,其实就是对状态变量的修改过程。如果处理一个-s, 就将s_state设置为TRUE,利用状态变量来决定最后的输出。对于处理技巧,注释基本都说了,不再赘述。

 

2.利用DFA处理代码行/注释行/空行

 

确定的有限自动机解题有一步是状态化简,去掉无效状态和多余状态,在整个过程中设置了5个状态,PRECODE, CODE, PRECOMMENT, COMMENT, SPACELINE,对于/* */ 多行注释的问题,最后利用状态变量influence进行纠正。时间问题,这段代码写的很丑,最后也没有优化,所有就只贴了部分进行说明。

最后利用处理结果对相应的统计变量进行自增运算。

 

 

六.测试设计过程

1. 路径测试

    main函数中if路径比较多,将每一条路径进行测试。

2.边界值测试

    在测试文件中敲入各种可能的注释和空行

3.等价类划分

  同类情况只测试一次。

 

#0help功能测试

start wc.exe wc.exe -help

 

#1基本功能测试

start wc.exe wc.exe -c -w -l test.c

 

#2基本功能测试---输出到指定的文件

start wc.exe wc.exe -c -w -l test.c -o output1.txt

 

#3检测用户的恶意输入(想输出全部结果但是没有输入-s)

start wc.exe wc.exe -c *.c -o output2.txt

 

#4检测用户的恶意输入(没有输入待检测的.c文件)

start wc.exe wc.exe -w -c -l

 

#5当前路径全部文件是否读取成功测试

start wc.exe wc.exe -s -c -w -l *.c -o output3.txt

 

#6检测用户输入错误的文件名

start wc.exe wc.exe -s -c -w -l finally.c -o output4.txt

 

#7复杂数据类型检测

start wc.exe wc.exe -s -a -c -w -l *.c -o output5.txt

 

#8停用词表检测

start wc.exe wc.exe -s -c -w -l *.c -o output6.txt -e stoplist.txt

 

#9检测使用停用词表命令出错

start wc.exe wc.exe -s -c -w -l *.c -o output7.txt stoplist.txt

 

#10检测输出到指定文件命令出错

start wc.exe wc.exe -s -c -w -l *.c  output8.txt

 

#11全部功能集成测试

start wc.exe wc.exe -s -a -c -w -l *.c -o output9.txt -e stoplist.txt

 

七. 参考网址

1.https://msdn.microsoft.com/zh-cn/library/ftsafwz3.aspx

2.http://www.cnblogs.com/berthua/p/7654892.html

 

posted on 2018-03-20 20:50  长江归海  阅读(445)  评论(0编辑  收藏  举报

导航