Word Count作業
Word Count作業
一.個人Gitee地址:https://gitee.com/Changyu-Guo
二.項目簡介
該項目主要是模擬Linux上面的wc命令,基本要求如下:
命令格式:
wc.exe [para] <filename> [para] <filename> ... -o <filename>
功能:
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
:將結果輸出到指定文件
要求:
-o
後面必須跟一個文件
-c -w -l
可以同時出現
-c -w -l
可以合並成-wcl
,即命令可以連寫如果不指定輸出文件,則將結果默認保存在result.txt裏面
三.PSP2.1表格
PSP2.1 | PSP階段 | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | 5 | 5 |
· Estimate | · 估計這個任務需要多少時間 | 5 | 5 |
Development | 開發 | 340 | 635 |
· Analysis | · 需求分析(包括學習新技術) | 20 | 30 |
· Design Spec | · 生成設計文檔 | 30 | 30 |
· Design Review | · 設計復審(和同事審核設計文檔) | 10 | 15 |
· Coding Standard | · 代碼規範(為目前的開發制定合適的規範) | 5 | 5 |
· Design | · 具體設計 | 15 | 20 |
· Coding | · 具體編碼 | 200 | 400 |
· Code Review | · 代碼復審 | 40 | 30 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 20 | 30 |
Reporting | 報告 | 60 | 50 |
· Test Report | · 測試報告 | 20 | 15 |
· Size Measurement | · 計算工作量 | 10 | 5 |
· Postmortem & Process improvement Plan | · 事後總結,並提出過程改進計劃 | 30 | 30 |
合計 | 405 | 690 |
四.解題思路
? 由於自己對C語言比較熟悉(主要是C語言編譯過後就是exe,其他語言還要打包,就直接用C語言寫了),因此選擇用C語言來實現這個項目。剛拿到題的時候仔細分析了一下,發現在功能上的要求不高,甚至不用校驗單詞的有效性,凡是以空格和逗號隔開的都算是單詞,因此第一次作業的難點應該在於命令行參數的解析上面。
? 接下來我用C語言寫了一個簡單的demo,嘗試著梳理一下程序構建思路,應該如何設計,模塊怎樣劃分。demo中所有的功能都在main函數裏面,沒有上傳到碼雲。
寫好demo後,大致整理了一下解題思路:
1.程序執行流程分析
? 根據項目的要求,該程序執行的大體流程為:首先用戶執行程序並附帶各種參數,程序首先要分析處理各種選項,校驗選項的有效性,並將各種參數和對應的文件聯系在一起,然後對不同的文件執行該文件對用的各種操作,然後將最終的結果一並保存在輸出文件中。
2.數據結構設計
? 根據對程序執行流程的分析,由於不同的文件對應著不同的操作,因此需要將文件名和其對應的操作綁定在一起,由此想到了用結構體保存一個文件的相關信息,然後使用鏈表將各個文件連起來。待命令處理完畢後,只需遍歷鏈表,即可對各個文件執行相應的操作。文件的結構體如下:
// 命令結構體 // 解析命令時存儲相關信息 struct Node { bool _c; bool _w; bool _l; bool _hasFile; char inFile[100]; int row; int character; int words; struct Node *next; };
3.模塊劃分
根據程序的執行流程,可以將程序劃分為以下幾個模塊:
(1).主函數
主函數中主要是一些基本的處理和一些簡單的邏輯的處理,負責調用其他函數
(2).命令處理模塊
? 對於用戶輸入的命令的處理,有很多種辦法,其中最常用的就是遍歷數組,或者將輸入的命令編程字符串,然後解析字符串,我選擇的是將用戶輸入的各種選項和命令拼接成一個字符串,然後遍歷整個字符串,並做相應的分析。
(3).統計模塊
? 統計模塊主要就是對每個文件做相應的統計操作,包括對行數的統計,對單詞數的統計,對字符數的統計,每個功能寫在一個單獨的函數裏面。統計完字符後順便將數據寫入文件。
五.關鍵代碼分析
1.命令處理函數
1 // 對用戶輸入的命令進行分析 2 // 傳入的用戶輸入的命令的字符串,中間用空格隔開 3 // 如果是-開頭的,則認為是選項 4 // 如果檢測到-o,就立即讀取後面緊跟的輸出文件 5 // 如果不是-開頭的,就認為是輸入文件 6 7 // 第二個參數是一串文件的頭結點 8 void analyseCommand(char commandStr[], struct Node *Head) 9 { 10 // 遍歷整個字符串 11 initFileNode(Head); 12 struct Node *cur; 13 cur = Head; 14 for (int i = 0;; i++) 15 { 16 // 讀出當前字符 17 char c = commandStr[i]; 18 // 如果遍歷到了\0,說明字符串結束,則退出函數 19 if (c == 0) 20 return; 21 // 如果c是-,則應該是一個選項 22 if (c == ‘-‘) 23 { 24 i++; 25 // 讀取出-後面的字符,並做判斷 26 read: 27 c = commandStr[i]; 28 // 如果-後面是c,就將_c置為true 29 if (c == ‘c‘) 30 { 31 cur->_c = true; 32 if (commandStr[++i] != ‘ ‘) 33 { 34 goto read; 35 } 36 continue; 37 } 38 // 如果-後面是w,就將_w置為true 39 else if (c == ‘w‘) 40 { 41 cur->_w = true; 42 if (commandStr[++i] != ‘ ‘) 43 { 44 goto read; 45 } 46 continue; 47 } 48 // 如果-後面是l,就將_l置為true 49 else if (c == ‘l‘) 50 { 51 cur->_l = true; 52 if (commandStr[++i] != ‘ ‘) 53 { 54 goto read; 55 } 56 continue; 57 } 58 // 如果-後面是o,則後面緊跟的一個參數一定是filePath 59 // 首先判斷後面是否有文件,如果有,就添加 60 // 如果沒有,就報錯 61 // 此時i的index是在選項上的 62 else if (c == ‘o‘) 63 { 64 i += 2; // 將i移動到 65 char next = commandStr[i]; 66 if (next == ‘-‘ || next == ‘0‘) 67 { 68 printf("after -o must a para\n"); 69 exit(-1); 70 } 71 char path[100] = ""; // 用來存放輸出路徑 72 for (int j = 0;; j++) 73 { 74 // 讀取出命令中的文件名中的每一個字符 75 char ch = commandStr[i++]; 76 77 // 如果讀取到了0,就說明文件名讀取結束,就退出 78 if (ch == ‘ ‘) 79 { 80 break; 81 } 82 path[j] = ch; 83 } 84 memset(outFile, 0, sizeof(outFile)); 85 strcpy(outFile, path); 86 } 87 else 88 { 89 // 如果-後面什麽都沒有,就判定為錯誤 90 printf("after - must a para\n"); 91 exit(-1); 92 } 93 } 94 else 95 { 96 // 如果不是-,則判定為輸入文件 97 // 此時i定位在輸入文件的第一個字符上 98 char path[100] = ""; 99 for (int j = 0;; j++) 100 { 101 char ch = commandStr[i++]; 102 if (ch == ‘ ‘) 103 { 104 break; 105 } 106 path[j] = ch; 107 } 108 strcpy(cur->inFile, path); 109 cur->_hasFile = true; 110 struct Node *fileNode; 111 fileNode = (struct Node *)malloc(sizeof(struct Node)); 112 initFileNode(fileNode); 113 cur->next = fileNode; 114 cur = fileNode; 115 i--; 116 } 117 } 118 // 檢測是否有輸入文件 119 // if (strlen(cur->inFile) == 0) 120 // { 121 // printf("you do not have input file"); 122 // exit(-1); 123 // } 124 }
代碼分析:該函數是這次作業中最重要的一個函數,因此單獨拿出來說一下。
要點說明:
1.使用for循環遍歷整個字符串
2.遇到-之後就認為是一個選項,就緊接著讀取他的後一個字符,如果是有效參數,就記錄在當前文件的結構體中,否則報錯
3.如果是-o,則認為後面緊跟著一個輸出文件,不做文件名有效性檢驗,不做權限檢查
4.如果是普通字符開頭,則認為是輸入文件,不做文件名有效性檢查,不做權限檢查
5.根據規則,輸出文件應該放在該文件對應參數的後面
6.遍歷完畢之後,就將相關數據都保存在了文件的結構體中,並連接成了鏈表,返回後可進行後期相關操作。
六.測試設計
根據要求,根據如下條件設計測試:
是否有輸入
是否輸入-
-後是否有參數
是否統計行數
是否統計字符數
是否統計單詞數
是否支持命令連寫
是否支持多文件統計
是否有-o
-o後是否跟文件
根據以上條件,設計了如下批處理文件:
1 .\wc.exe 2 .\wc.exe - 3 .\wc.exe -l 4 .\wc.exe -c 5 .\wc.exe -w 6 .\wc.exe -lc 7 .\wc.exe -lw 8 .\wc.exe -cw 9 .\wc.exe -lcw 10 .\wc.exe -lcw -o 11 .\wc.exe -lcw -o res.txt 12 .\wc.exe -lcw file1.c 13 .\wc.exe -lcw file1.c -o 14 .\wc.exe -lcw file1.c -o res.txt 15 .\wc.exe -lcw file1.c file2.c -o res.txt 16 .\wc.exe -lcw file1.c -lcw file2.c -o res.txt 17 .\wc.exe -lcw file1.c -o -lcw file2.c -o res.txt 18 PAUSE
測試結果如下:
文件輸出結果:
七.參考文獻
《構建之法--現代軟件工程》 --鄒新 [第三版]
博客園把我的格式變成了這個樣子
(哇的一聲就哭出來了)
Word Count作業