wordcount2--realized by c

wc.exe 编写说明

先给出:码云地址 github地址node

说明:给出git地址是由于码云不支持TOC标签,我直接写锚定代码他也不支持,可是github却能够,因此加上一个github地址linux

博文目录

git不支持TOC标签生成目录(当场翻白眼)(gitee和github都不支持)git

可是好的是,github仍是能够实现锚定功能的,可是gitee殊不知道为何不行,因此我也给了一个github的地址。github

正式提交后的更新说明

  1. 用例10没有经过,可是我也没有修改,昨天失眠,想了想算法,有点小想法,因此修改了代码;
  2. 昨天在想若是后面要完成别的需求,那么在个人代码上加东西彷佛个人代码不是一个易重构的代码(我把这点加入了问题中);
  3. 再次阅读做业的说明,发现个人输出不合要求,已改正;
  4. 看到模板博客有生成的目录,因此加上了目录;
  5. 看到老师给一个同窗说能够把测试写成脚本,这样就能够自动化测试,我以为用shell写个小脚本这样最方便了,因此加上这个东东;

项目代码目录

.
├── bin
│   ├── linux_amd64
│   │   ├── input
│   │   ├── my.ini
│   │   ├── result.log
│   │   ├── result.txt
│   │   └── wc
│   └── windows
│       ├── wc32.exe
│       └── wc64.exe
├── image
│   └── whole_flow_chart.png
├── readme.md
└── src
    ├── function.c
    ├── wc.c
    └── wc.h
  • bin:存放编译后的文件
    • linux_amd64文件夹:在64位linux下编译的可执行文件
    • windows:在windows下编译的可执行文件
  • image文件夹:存放的是readme文件中须要的图片说明文件(cnblog中直接把图片拖到富文本编辑器中便可自动上传)
  • readme.md:项目说明文件,记载着和项目相关的各项文字(我感受是我唠嗑的地方)
  • src文件夹:存放源文件

1 须要说明

1.1 概要

wc接收一个文本文件,并统计这个文本文件中的信息(行数、字数等)算法

1.2 基本功能

wc.exe -c file.c //返回文件 file.c 的字符数shell

wc.exe -w file.c //返回文件 file.c 的单词总数windows

wc.exe -l file.c //返回文件 file.c 的总行数markdown

wc.exe -o outputFile.txt file.c //将结果输出到指定文件outputFile.txtapp

我在原来的基本上加了一个帮助选项,由于以为这样更适用:

wc.exe -h //显示帮助信息

注意:

  • 空格,水平制表符,换行符,均算字符。
  • 由空格或逗号分割开的都视为单词,且不作单词的有效性校验,例如:thi#,that视为用逗号隔开的2个单词。
  • -c, -w, -l参数能够共用同一个输入文件,形如:wc.exe --w --c file.c
    。>
  • -o
    必须与文件名同时使用,且输出文件必须紧跟在-o参数后面,不容许单独使用-o参数

2 基本思路

2.1 接受参数

参数的形式有两种:长选项、短选项,选项间能够组合,长选项用空格隔开各个参数

例: wc.exe --word --charater file.c

短选项能够多个直接叠加或者,也像长选项同样分开写

例: wc.exe -wc file.c wc.exe -w -c file.c

对于一个命令行程序,它能够接受来自命令行的参数。

c语言的main函数中有两个参数:int main (int argc, char
*argv[])
,这两个参数就是用于这个参数的输入。

argc 一个整数,表明有多少个命令行参数,在此注意两个点

​ 一、 参数间是用空格格开的;

​ 二、程序名是第一个参数。

argv[i]是一个指针,指向第i个参数的首地址

理解了以上的两个参数就简单了,只须要作一些基本的字符串处理就能够了。

2.2.1 -h参数

这个参数单独说是由于这个参数不能和别的参数混用,因此我在程序里面是单独写的,一开始就判断是否用户须要的是help帮助,若是是的话,那么彻底没必要要再运行程序(打开文件),直接exit停止进程

2.2.2 -w -c -l 参数

这三个参数都是一个路数:

一、打开文件;

二、判断要作的操做;

三、执行操做。

它们间只有步骤3是不一样的,因此有理由把3写成不一样的函数,再由2判断执行哪一个。

有一些细节问题是能够考虑的。

好比,由于单复数的关系,有一行/个 单词/字母,应该是不一样的表达(是否有s)

额外就是判断一个单词的算法也是值得考虑的问题,个人想法是,若是一个字符,它本身是一个字母,它前面是一个非字母,那么这就是一个单词的标致。

2.2.3 -o 参数

这个参数比较特殊,由于它后面跟了一个文件,要作的事情是把输出的内容存到的文件换成用户自定义的名字。

总的来讲是两件事情

  • 捕获用户输入的文件的名字,并建立这个文件;
  • 把输出的信息存进去。

3 程序流程图

4 关键代码

4.1 搜索行数

void
lines_searching(FILE *fp, bool out_to_files){
    int line = 0;
    int ch = 0;

    while(!feof(fp)){
        ch = fgetc(fp);
        //putchar(ch);
        if(ch == '\n') line++;
        else continue;
    }//of while loop

    //printf("line is %d\n",line);
    
    output_to_sdo(line, "line", out_to_files);
}//of character_searching function

4.2 查找词数

void
words_searching(FILE *fp, bool out_to_files){
    int letter =0;
    int word = 0;
    bool is_word = FALSE;

    while(!feof(fp)){
        letter = fgetc(fp);

        if((letter >= 'a' && letter <= 'z') || (letter >= 'A' && letter <= 'Z' ) || (letter == '\'')){
            if(!is_word)
                //if if_word is false, it means this is a brand new word
                is_word = TRUE;
        }//of if
        else{
            if(is_word){
                word ++;
                is_word = FALSE;
            }//of if
            else continue;
        }//of else
    }//of while loop

    output_to_sdo(word, "word", out_to_files);
}//of words_searching function

这个里面有一些问题要考虑:

  • 前面提到的-w选项里面对于单词的介定是什么?
    • 一个新单词的标致:当前字符是非字母,而且上一个字符是字母。
  • 进一步考虑
    • that1but算一个单词仍是两个单词?th3at算一个单词仍是两个单词?
    • I don't like you算几个单词?
    • "do you like me?"he asked 算这个词?
    • 原题中说只要是空格和逗号分开的都算两个词,若是出实换行和不可显特殊字符(没能从键盘键入的字符)怎么处理?
    • 可显的特殊字符单独出现怎么处理?
      • 例1: He died. ? No!
      • 例2: He died. . Ah-ha?
  • 简单考虑这个问题:只有换行(\n)、制表(\t)、空格(
    )可分以分开单词,把上面的判断字符代码改为:
letter != '\n' || letter != '\t' || letter != ' '

4.3 输出函数(包括输出到标准输入输出和文件)

void
output_to_sdo(int number, char* name, bool out_to_files){
        char result_fp_name[50];//the name of the result files

        char result[50];
        //printf("%s\n",specific_file);
        if(number == 0) sprintf(result, "No %s in the file\n", name);
        else if(number == 1) sprintf(result, "1 %s in the file\n", name);
        else sprintf(result, "%d %ss in the file\n",number,name);

        printf("%s", result);

        if(out_to_files){
            strcpy(result_fp_name, specific_file);
        }//of if
        else{
            strcpy(result_fp_name, "result.txt");
        }//of else

        FILE *result_fp = fopen(result_fp_name,"w");

        fputs(result, result_fp);
        fclose(result_fp);
}//of function output_to_fdo

5 测试

主要是作简单的系统测试,等价类和边界值

等价类、边界值

从输入的数据入用进行分析,本程序的输入信息就是从命令行接受的一些数据,这些个数据能够分为两人类

  1. 选项:控制程序运行的方式和输出的方式,这类选项能够分为三类

    ​ 1.1 -h
    :这个选项不能和别人一用,并且这个选项不带参数,它也不容许程序再带参数;

    ​ 1.1 -o
    :这个选项须要和别人组合在一块儿用,并且不能单用,在这个选项后面须要带一个参数,并且也须要程序带一个参数;

    ​ 1.2
    -w/-c/-l:它们都是是执行特色的查找任务的,这几个选项不带参数,可是须要程序带一个参数。

  2. 参数:在本程序中也就是须要查找的文件,分红这文件存在、不存在。

    输入条件 有效等价类 无效等价类
    选项 -h (1)/ -w(2) / -l(3) / -c(4) / -o out(5) / -w -c(6) -a(7) / -p(8) / -h -w(9)
    参数 文件(10) 没有文件(11)

自制的待统计文本

I loved you.
But you didn't love me.
I felt sad.
But you seemed to feel happy.
So I begun to hate you.

5.1 user-case 1

seven@mylab:~/wordcount/bin/linux_amd64$ cat result.in 
Usage: wc.exe [OPTIONS]... [FILE]

--character    -c     calculate the numbers of characters in the file
--word         -w     calculate the numbers of words in the file
--line         -l     calculate the numbers of lines in the file
--outtofile=x  -o x   transfer the result to the specific file

5.2 user-case 2

seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -h input
Wrong Parameter!

5.3 user-case 3

seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -w input
23 words in the file
seven@mylab:~/wordcount/bin/linux_amd64$ wc -w input
23 input

与linux下自带的wordcount(wc)的结果对比了下

5.4 user-case 4

seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -w nothing
Fail to Open the File!
seven@mylab:~/wordcount/bin/linux_amd64$ wc -w nothing
wc: nothing: No such file or directory

5.5 user-case 5

seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -l input 
5 lines in the file
seven@mylab:~/wordcount/bin/linux_amd64$ wc -l input 
5 input

5.6 user-case 6

seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -l nothing
Fail to Open the File!
seven@mylab:~/wordcount/bin/linux_amd64$ wc -l nothing
wc: nothing: No such file or directory

5.7 user-case 7

seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -c input 
104 characters in the file
seven@mylab:~/wordcount/bin/linux_amd64$ wc -c input 
103 input

出现了不一样

5.8 user-case 8

seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -c nothing
Fail to Open the File!
seven@mylab:~/wordcount/bin/linux_amd64$ wc -c nothing
wc: nothing: No such file or directory

5.9 user-case 9

seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -w -o result.log input 
23 words in the file
seven@mylab:~/wordcount/bin/linux_amd64$ cat result.log 
23 words in the file
seven@mylab:~/wordcount/bin/linux_amd64$ wc -w input > result.log 
seven@mylab:~/wordcount/bin/linux_amd64$ cat result.log 
23 input

5.10 user-case 10

seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -w -h
Fail to Open the File!
seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -w -h nohting
Fail to Open the File!
seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -w -h input 
Wrong Parameter!
23 words in the file

这个用例没有经过

  • 修改了代码,在检测参数的时候,若是查到了-h,则应该报错
for(int i = 1; i < argc-1; i++){
    if(argv[i][0] == '-'){
        switch(argv[i][1]){
            case 'c':
                print_characters = TRUE;
                break;
            case 'l':
                print_lines = TRUE;
                break;
            case 'w':
                print_words = TRUE;
                break;
            case 'o':
                out_to_files = TRUE;
                strcpy(specific_file,argv[i+1]);
                //printf("%s\n",specific_file);
                break;
            case 'h'://add a case to detect -h
                printf("-h parameter should work alone!\n");
                exit(0);
            default:
                printf("Wrong Parameter!\n");
        }//of switch
    }//of if
}//of for loop
seven@mylab:~/wordcount/src$ ./a.out -h input 
-h parameter should work alone!

经过测试!

5.11 user-case 11

检查用例7

  • 手动数了一下可见字符是98个,再加上五行有六个段断的控制符,彷佛我是真的错了。
  • 再写了一个简单的文本
seven@mylab:~/wordcount/bin/linux_amd64$ printf "I hate you\nYou hate me\n"
I hate you
You hate me
seven@mylab:~/wordcount/bin/linux_amd64$ printf "I hate you\nYou hate me\n" > my.ini
seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -c my.ini 
24 characters in the file
seven@mylab:~/wordcount/bin/linux_amd64$ wc -c my.ini 
23 my.ini
  • 不信邪,再来一次
seven@mylab:~/wordcount/bin/linux_amd64$ echo "I really made a mistake??" > my.ini 
seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -c my.ini 
27 characters in the file
seven@mylab:~/wordcount/bin/linux_amd64$ wc -c my.ini 
26 my.ini
  • 三次的测试个人结果稳定的比正确结果少一个,有理由相信这是一个系统偏差,是程序的错误。
  • 查程序发现的缘由,如下是代码
while(!feof(fp)){
    fgetc(fp);
    character ++;
}//of while loop

这样把最后一个结尾字符也记上了,因此应该把结果减去1.

5.12 user-case 12

  • 从新编译后再测试一下这个功能
seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -w my.ini 
5 words in the file
seven@mylab:~/wordcount/bin/linux_amd64$ wc -w my.ini 
5 my.ini
seven@mylab:~/wordcount/bin/linux_amd64$ ./wc -w input 
23 words in the file
seven@mylab:~/wordcount/bin/linux_amd64$ wc -w input 
23 input

5.13 user-case 13

  • 输出格式不对,因此改了一下格式(惧怕中文出问题因此我全用了英文)
seven@mylab:~/wordcount/src$ ./a.out -c input 
input, character number:23

5.14 自动测试脚本

#!/bin/sh

#Author: seven
#date: 2018/09/27
#aim: an automatic script to test the program

#a function to print test result
#function will receive 2 parameters,
#first is the type of the test,
#second is whether the test tested as expected
test_result_print(){
    if [ $2 -eq 1 ]
    then
        echo `date "+%Y-%m-%d %H:%M:%S"` " $1 test succeeded!" | tee -a test_log.txt
    else
        echo `date "+%Y-%m-%d %H:%M:%S"` " $1 test met with failure!" | tee -a test_log.txt
    fi
}

compare_result_with_standard(){
    if [ "$1" = "$2" ]
        then 
            if_test_success=1
        else 
            if_test_success=0
    fi
}

test_file_name="input"

#Here is the test for ./wc -h
#the ideal result is the usage of the program
standard_result="Usage: wc.exe [OPTIONS]... [FILE]

--character    -c     calculate the numbers of characters in the file
--word         -w     calculate the numbers of words in the file
--line         -l     calculate the numbers of lines in the file
--outtofile=x  -o x   transfer the result to the specific file"
result=`./wc -h`

compare_result_with_standard "$result" "$standard_result"
test_result_print "wc -h" $if_test_success

#Here is the test for ./wc -h input
#the ideal result is to print "-h parameter should work alone!"
standard_result="-h parameter should work alone!"
result=`./wc -h input`

compare_result_with_standard "$result" "$standard_result"
test_result_print "wc -h print" $if_test_success

#Here is the test for wc -w input
standard_result=`wc -w $test_file_name | sed 's/[^0-9]//g'`
result=`./wc -w $test_file_name | sed 's/[^0-9]//g'`

compare_result_with_standard "$result" "$standard_result"
test_result_print "wc -w input" $if_test_success

#Here is the test for wc -w nothing
standard_result="Fail to Open the File!"
result=`./wc -w nothing`

compare_result_with_standard "$result" "$standard_result"
test_result_print "wc -w nothing" $if_test_success

#Here is the test for wc -l nothing
standard_result="Fail to Open the File!"
result=`./wc -l nothing`

compare_result_with_standard "$result" "$standard_result"
test_result_print "wc -l nothing" $if_test_success

#Here is the test for wc -c input
standard_result=`wc -c $test_file_name | sed 's/[^0-9]//g'`
result=`./wc -c $test_file_name | sed 's/[^0-9]//g'`

compare_result_with_standard "$result" "$standard_result"
test_result_print "wc -c input" $if_test_success

#Here is the test for wc -c nothing
standard_result="Fail to Open the File!"
result=`./wc -c nothing`

compare_result_with_standard "$result" "$standard_result"
test_result_print "wc -c nothing" $if_test_success

#Here is the test for wc -w -o result.log input
standard_result=`wc -w $test_file_name | sed 's/[^0-9]//g'`
result=`./wc -w -o result.log $test_file_name | sed 's/[^0-9]//g'`

compare_result_with_standard "$result" "$standard_result"
test_result_print "wc -w -o result.log input in std output" $if_test_success

result=`cat result.log | sed 's/[^0-9]//g'`

compare_result_with_standard "$result" "$standard_result"
test_result_print "wc -w -o result.log input in outputfile" $if_test_success

测试结果:

seven@mylab:~/wordcount/bin/linux_amd64$ ./test.sh 
2018-09-27 16:11:58  wc -h test succeeded!
2018-09-27 16:11:58  wc -h print test succeeded!
2018-09-27 16:11:58  wc -w input test succeeded!
2018-09-27 16:11:58  wc -w nothing test succeeded!
2018-09-27 16:11:58  wc -l nothing test succeeded!
2018-09-27 16:11:58  wc -c input test succeeded!
2018-09-27 16:11:58  wc -c nothing test succeeded!
2018-09-27 16:11:58  wc -w -o result.log input in std output test succeeded!
2018-09-27 16:11:58  wc -w -o result.log input in outputfile test succeeded!

6 还存在的问题

6.1 选项组合问题

  • 本文前对短选项的定义中有一个这样的组合方式:
    -wl,可是实际上没去实现它(懒);
  • 长选项也没去实现,由于要字符串比较太麻烦了,并且我还要准备考研。

6.2 程序的实现的算法问题

  • 对因而否要把程序的结果输出到文件里面我选择了用一个控制变量进行操做,而后用全局的变量存储文件地址引用到函数中,彷佛应该有更好的方法(我特别想看GNU的实现可是我,是在看不懂+找不到源函数);
  • 代码的重用性不太好;

7 总结

  • 开心的是有陈老师缘由让咱们去学习git,去学习写博客,让我一个大四的老学长也能学到一些东西(已经会用github写日记了);
  • 大学以来一直有个遗憾,就是没有遇到几个好老师认真布置课程设计,让又有上课的知识,又要本身去学习一部分的东西,此前我也没有上课前预习,下课后复习,上课中记笔记的习惯,陈老师的此次课让我挺开心的是,我开始作了,让我在大四的时候找到了点大学的感受(愿个人研究生之路更好~);
  • 想把程序写得更好,可是我用c语言处理的效率比较低,原本想参考GNU
    LINUX对于WC的编写,无耐我看不懂他们的代码,因而乎,我只能本身先实现一些小功能,形式上和功能上没办作到还不错的样子;
  • 没想到本身写博客仍是有不少废话能够说的。

8 参考、致谢

完成一个还算比较难的程序(由于我总想着GNU写过了的wc)对一个学生而言并不是易事,参考了不少(其实也很少),见下:

相关文章
相关标签/搜索