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)對一個學生而言並不是易事,參考了不少(其實也很少),見下:

相關文章
相關標籤/搜索