1. 程式人生 > >Word Count程序(C語言實現)

Word Count程序(C語言實現)

地址 組成 other 進一步 and 文件遍歷 stand evel oid

Word Count 程序

GitHub地址:https://github.com/MansonYe/Word-Count


一、項目簡介

Word Count 是用以統計文本文件的字符數、單詞數和行數的常用工具。

二、功能分析及實現情況

· 基本功能:

統計file.c的字符數(實現)

統計file.c的單詞數(實現)

統計file.c的行數(實現)

· 拓展功能:

遞歸處理目錄下符合類型的文件(實現)

顯示代碼行、空行和註釋行的行數(實現)

支持通配符(* , ?)(實現)

· 高級功能:

支持GUI界面並顯示詳細信息(待實現)

· 定義:

  字符:可顯示的ASCII碼字符,因此不包括空格和‘\n’等控制字符

  單詞:由一串連續英文字母組成,遇到英文以外為單詞的分隔

  行:每行以分行符或結束符為標誌,分為三種:

  空行:本行只由非顯示字符組成,若有代碼,則不超過一個可顯示字符

  代碼行:本行包括多於一個字符的代碼

  註釋行:本行不是代碼行,且包括註釋

· 例子:

如圖為一個典型的C語言文本文件

技術分享圖片

所有顯示的字符均為納入字符計算中:

如2行有19個字符

以非英文字母分隔單詞:

如10行單詞數為3,但7行單詞數為0

包含多於一個代碼的行為代碼行:

如10、14、21行等均為代碼行

不是代碼行且包含註釋為註釋行:

如4、5、22、24行等,6行因為在文檔型註釋中顧算註釋行

沒有顯示字符或只有一個代碼的行為空行:

如1、3、15行,但6行在文本註釋中因此不算作空行,算作註釋行

三、PSP

PSP

Personal Software Process Stages

預估耗時(分鐘)

實際耗時(分鐘)

Planning

計劃

30

30

Estimate

· 估計這個任務需要多少時間

10

10

Development

開發

480

600

Analysis

· 需求分析 (包括學習新技術)

60

70

Design Spec

· 生成設計文檔

5

5

Design Review

· 設計復審 (和同事審核設計文檔)

30

50

Coding Standard

· 代碼規範 (為目前的開發制定合適的規範)

10

10

Design

· 具體設計

60

65

Coding

· 具體編碼

480

540

Code Review

· 代碼復審

60

75

Test

· 測試(自我測試,修改代碼,提交修改)

60

120

Reporting

報告

120

120

Test Report

· 測試報告

30

60

Size Measurement

· 計算工作量

10

5

Postmortem & Process Improvement Plan

· 事後總結, 並提出過程改進計劃

30

60

四、解題思路及功能實現:

字符統計:

遍歷文檔字符,通過排除非顯示字符,統計顯示字符數量;

單詞統計:

遍歷文檔字符,利用變量記錄字符是否為英文字母狀態,統計進入該狀態次數即為單詞詞數

行數統計:

遍歷文檔以行為單位的字符串,遍歷次數即為行數

特殊行數統計:

遍歷文檔以行為單位的字符串,再利用指針遍歷字符串;首先判斷是否為代碼行(優先級最高),其次判斷是否為註釋行,由於三種行互斥,顧空行數為總行數減去前兩者。通過變量記錄狀態以判斷代碼行和註釋行。

通過遞歸實現文件遍歷:

利用_findfirst,_findnext,_findclose函數實現當前文件夾文件遍歷,通過attrib屬性確定文件夾,通過加工字符串,將新字符串作為新參數調用自身實現通過遞歸進入下一級目錄。同時通過加工字符串在路徑後加上文件名作為參數傳遞給統計函數從而實現每個文件的Word Count。

五、關鍵代碼:

基本功能:

int CodeCount(char *Path) {    //計算字符個數
    
    FILE *file = fopen(Path, "r");
    assert(file != NULL);    //若文件不存在則報錯

    char code;
    int count = 0;

    while ((code = fgetc(file)) != EOF)     //讀取字符直到結束
            count+= ((code !=  ) && (code != \n) && (code != \t));    //判斷是否是字符    

    fclose(file);

    return count;
}

int WordCount(char *Path) {    //計算單詞個數

    FILE *file = fopen(Path, "r");
    assert(file != NULL);

    char word;
    int is_word = 0;    //用於記錄字符是否處於單詞中
    int count = 0;

    while ((word = fgetc(file)) != EOF) {
        if ((word >= a && word <= z) || (word >= A && word <= Z)) {    //判斷是否是字母            
            count += (is_word == 0);
            is_word = 1;    //記錄單詞狀態
        }
        else 
            is_word = 0;    //記錄不處於單詞狀態
    }
    fclose(file);

    return count;
}

int LineCount(char *Path) {    //計算行數

    FILE *file = fopen(Path, "r");
    assert(file != NULL);

    char *s = (char*)malloc(200 * sizeof(char));
    int count = 0;
    
    for (; fgets(s, 200, file) != NULL; count++);    //逐次讀行

    free(s);
    fclose(file);

    return count;
}


特殊行數統計:

void AllDetail(char *Path) {    //顯示空行, 代碼行,註釋行
    
    FILE *file = fopen(Path, "r");
    assert(file != NULL);

    char *s = (char*)malloc(200 * sizeof(char));//申請空間
    int i;    
    int is_codeline = 0;    //狀態記錄變量
    int is_annoline = 0;    
    int AnnoLock = 0;
    int AnnoFileLock = 0;
    
    int codecount = 0;
    int annocount = 0;
    int blankcount = 0;
    
    while (fgets(s, 200, file) != NULL) {    //逐次取文件中的行
        for (i = 0; *(s+i) != \0; i++) {
            
            if ( ( ( *(s+i) >= a && *(s+i) <= z) || ( *(s+i) >= A && *(s+i) <= Z) ) && AnnoFileLock == 0) {//判斷是否是代碼行
                codecount += (is_codeline == 0 && AnnoLock == 0);    //進入代碼行的時候代碼行加一                    
                is_codeline = 1;
            }

            if ( *(s+i) == / && *(s+i+1) == / && is_codeline == 0 && AnnoFileLock == 0){    //判斷是否為註釋行
                    annocount++;
                    AnnoLock = 1;
            }

            if (*(s + i) == / && *(s + i + 1) == *){    //判斷文檔註釋開始
                AnnoFileLock = 1;
                annocount -= is_codeline;    //註釋在代碼後不算註釋行,因此減一
            }

            if (*(s + i) == * && *(s + i + 1) == /) {    //判斷文檔註釋結束
                AnnoFileLock = 0;
                annocount += (*(s + i + 2) == \n);    //註釋後換行情況
            }
        }        
        annocount += AnnoFileLock;    //註釋行結束時算作註釋行加一

        blankcount++;    //每一行結束計數加一,並清空狀態
        is_codeline = 0;
        is_annoline = 0;
        AnnoLock = 0;
    }
    free(s);
    fclose(file);

    blankcount = blankcount - codecount - annocount;    
    printf("codeline:%d, annoline:%d, blankline:%d\n", codecount, annocount, blankcount);
}

通過遞歸實現文件遍歷:

void Scan(char *Path, char Type) {
    char *FileName = NULL;
    char *FileType = NULL;
    char Temp[30];    //用於暫存改變得字符串
    long Head;
    struct _finddata_t FileData;
    int i = 0;

    FileName = Path;
    while (*(Path + i) != \0) {    //找出文件名和文件類型的位置
        if (*(Path + i) == \\)
            FileName = Path + i + 1;
        if (*(Path + i) == .)
            FileType = Path + i + 1;
        i++;
    }
    
    strcpy(Temp, FileType);//調整字符串
    *FileType = *;
    *(FileType + 1) = \0;
    
    Head = _findfirst(Path, &FileData);
    
    strcpy(FileType, Temp);//恢復字符串

    do {
        if ( !strcmp(FileData.name, "..") || !strcmp(FileData.name, "."))    //去除前驅文件路徑
            continue;
        
        if (_A_SUBDIR == FileData.attrib)    //是文件夾
        {    
            strcpy(Temp, FileName);    //調整字符串
            for (i = 0; *(FileData.name + i) != \0; i++) {
                *(FileName + i) = *(FileData.name + i);
            }
            *(FileName + i) = \\;
            *(FileName + i + 1) = \0;
            strcat(Path, Temp);

            Scan(Path, Type);

            strcpy(FileName, Temp);    //恢復字符串            
        }
        else//是文件 
        {    
            for (i = 0; *(FileData.name + i) != .; i++);
            if (!strcmp(FileData.name + i + 1, FileType)) {    //是指定類型的文件
                
                strcpy(Temp, FileName);
                strcpy(FileName, FileData.name); //調整字符串
                
                printf("%s:  ", FileData.name);
                Run(Type, NULL, Path);    //將地址及功能傳到啟動函數
                printf("\n");

                strcpy(FileName, Temp);//恢復字符串
            }
        }
    } while (_findnext(Head, &FileData) == 0);

    _findclose(Head);    
}

啟動函數:

void Run(char Type, char Type2, char *Path) {
    
    switch (Type) {
    case c: printf("code count: %d\n", CodeCount(Path)); break;
    case w: printf("word count: %d\n", WordCount(Path)); break;
    case l: printf("line count: %d\n", LineCount(Path)); break;
    case a: AllDetail(Path); break;
    case s: Scan(Path, Type2); break;
    default: printf("type input error"); break;
    }
}

六、測試結果

字符數計算測試:

技術分享圖片

單詞數計算測試:

技術分享圖片

行數計算測試:

技術分享圖片

多種行數計算測試:

技術分享圖片

遍歷功能測試:

技術分享圖片

測試用文件:

技術分享圖片

技術分享圖片

test1:

test2:

c

test3:

word

test4:

/*this is a test file*/

/*
manson ye 

2018/09/09
*/

#include<stdio.h>
#include<string.h>


int main() //main method
{    
    int i = 100 / 4;
    
    printf("hello world");/**/
    /*
    test
    */return 0;
}    //this is test file

//this is test file with many kinds of annotations

test5:

another test file


asasas




七、小結

自己比較少按正常的順序進行項目的開發。這次的機會令我再次認識到做好事前分析和安排的重要性,根據安排進行開發,有效地提高了開發過程中的可見性。

一開始準備的時候考慮過使用其他語言,但設計到了分辨註釋行的時候,由於指針能發揮巨大作用最終還事選擇了C語言

寫程序的中途我學到了不少新知識,特別是在文件遍歷這一方面,並得到了一定程度的實踐經驗。

同時我也認識到自己關於測試方法知識的匱乏,雖然這次早早地就寫完了程序,但是為了搞測試項目,花了很多時間但最後還是只能手動測試,還接近了時間限制,因此眼下應該先學會如何測試項目的正確使用方法。

經過這次作業,我認為只有通過不斷地實踐與學習,才能夠結合知識與實踐,進一步提升自己的能力。

ps:第一次寫博客,不知道原來能直接用html寫,下次應該會呈現得更好!

Word Count程序(C語言實現)