github项目地址:https://github.com/insomniali/wc
- 基本功能
- wc.exe -c file 统计文件file的字符数 【实现】
- wc.exe -w file 统计文件file的词的数目 【实现】
- wc.exe -l file 统计文件file的行数 【实现】
- 扩展功能
- wc.exe -d file 递归显示目录下符合条件的文件 【实现】
- wc.exe -s file 递归统计目录下符合条件的文件的总字符,词和行数 【实现】
- wc.exe -a file 统计文件file的空白行,注释行,代码行 【实现】
- 高级功能
- wc.exe -x 图形化界面 【未实现】
设计思路
根据需求,程序主要功能可分为两部分,一是接收输入指令并解析,二是对文件执行指令所对照的操作。通过程序内置参数,再调用相应类方法即可满足需求。
字符统计函数:
定义:除转义字符,数字及字母外所有字符
方法:通过getline()获取一行数据,再由字符的ASCII码来判断
单词统计函数:
定义:“a-z”,"A-Z"组成的单个字母或多个连续字母组成的单词
方法:通过getline()获取一行数据,再由字符的ASCII码来判断,在统计完第一个字母后置continuectrl为真,直到统计到第一个不符合定义的字符
行数统计函数
定义:通过getline()判断,不为空则为有效行
方法:grtline()
空白,注释,代码行数统计函数
定义:
空白行:字符串长度为0或长度为1,字符为(;,{,})之一
注释行:以“{//”,“}//”,“;//”,“//”为字符串开头的行
代码行:除空白行,注释行以外所有行
方法:
空白行:用string.length()统计字符串长度,符合定义即为空白行
注释行:获取字符串前三位字符,根据结果是否符合定义判断
代码行:总行数减去空白行数及注释行数
架构图
设计了一个WCclass类,类成员变量是int类型的各种统计数据,类成员方法则是统计各种数据
代码说明
主函数main()
int main(int argc ,char* argv[]) { string filepath = "C:\\Users\\L\\Desktop\\软件工程\\test"; //测试路径 string filename; string temp; string operateword; string paramater=argv[1]; WCclass * test=new WCclass(); for (int i = 2; i < argc + 1; i++) { temp = filepath; filename = argv[i]; filename = temp + "\\" + filename; test->fileoperate(filename, paramater); }
detele test; return 0; }
指令解析与文件操作函数()
void WCclass::fileoperate(string filename, string paramater) { ifstream fin(filename.c_str()); //获取文件名,打开输入流 char ch; //每次判断一个字符 string str; //保存行数据 char **strs; //保存字符串str以“ ”分割的字符串 //可输入参数类型 string paramater1 = "-c"; string paramater2 = "-w"; string paramater3 = "-l"; string paramater4 = "-d"; string paramater5 = "-s"; string paramater6 = "-a"; strs = (char**)malloc(sizeof(char) * 256); while (getline(fin, str)) { int strcount = 0; rowcount(); if ((str.length() == 0) || ((str.length() == 1)&& (str[strcount] == (char)"{")) || ((str.length() == 1) && (str[strcount] == (char)"}"))) { emptyrowcount(); continue; } strs[strcount] = strtok((char*)str.c_str(), " "); while (strs[strcount] != NULL) { strcount++; strs[strcount] = strtok(NULL, " "); } for (int i = 0; i < strcount; i++) { string temp = strs[i]; int continuectrl = 0;//控制单词连续 int dqmoff = 0;//是否是;,{,} int flag = 0;////是否相邻 for (int j = 0; j < temp.length(); j++) { ch = temp[j]; if ((ch >= 33 && ch <= 47) || (ch >= 58 && ch <= 64) || (ch >= 91 && ch <= 96) || (ch >= 123 && ch <= 126)) { charcount(); continuectrl = 0; //注释行//的情况 if (ch == 47) { if (j == 0) { flag = 1; } else if (j == 1) { if (flag == 1) { flag = 0; noterowcount(); } } } //注释行({//,}//,;//)的情况 if ((ch == 59) || (ch == 123) || (ch == 125)) { if (j == 0) { dqmoff = 1; continue; } } if (dqmoff == 1) { if (ch == 47) { if (flag == 1) { flag = 0; noterowcount(); } else flag = 1; } else { dqmoff = 0; } } } else if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122)) { if (continuectrl == 1)continue; continuectrl = 1; wordcount(); } else if (ch >= '0'&&ch <= '9') { digitcount(); } else { cnwordcount(); } } } } if (paramater == paramater1)std::cout << "字符数为:" << charnums + cnwordnums / 2 << endl; else if (paramater == paramater2)std::cout << "单词数为:" << wordnums << endl; else if (paramater == paramater3)std::cout << "行数为:" << rownums << endl; else if (paramater == paramater4)listFiles(filename.c_str()); else if (paramater == paramater5) { filegroup = (string*)malloc(sizeof(string) * 256); filegroup=listFiles(filename.c_str(), filegroup); //获取递归处理文件的文件名数组 for (int i = 0; filegroup[i].length() != 0; i++) { std::cout << filegroup[i] << endl; fileoperate(filegroup[i], paramater1); fileoperate(filegroup[i], paramater2); fileoperate(filegroup[i], paramater3); } std::cout << endl; std::cout << "总字符数为:" << charnums + cnwordnums / 2 << endl; std::cout << "总单词数为:" << wordnums << endl; std::cout << "总行数为:" << rownums << endl; } else if (paramater == paramater6) { std::cout << "空行数为:" << emptyrow << endl; std::cout << "注释行数为:" << noterow << endl; std::cout << "代码行数为:" << rownums-emptyrow-noterow << endl; } fin.clear(); fin.close(); free(strs); }
递归处理函数ListFiles()
void listFiles(const char * dir) { char dirNew[200]; strcpy(dirNew, dir); intptr_t handle; //intptr_t是为了跨平台,其长度总是所在平台的位数,用来存放地址。 _finddata_t FileInfo; // _finddata_t 是用来存储文件各种信息的结构体。 handle = _findfirst(dirNew, &FileInfo); //第一个参数为文件名,可以用"*.*"来查找所有文件,也可以用"*.cpp"来查找.cpp文件。第二个参数是_finddata_t结构体指针。若查找成功,返回文件句柄,若失败,返回-1。 if (handle == -1) // 检查是否成功 return; do { if (FileInfo.attrib & _A_SUBDIR) //如果文件是目录 { if (strcmp(FileInfo.name, ".") == 0 || strcmp(FileInfo.name, "..") == 0) //如果文件名和当前目录和父目录相同则跳过,否则会只输出当前目录 continue; std::cout << FileInfo.name << "\t<目录>\n"; // 在目录后面加上"\\"和搜索到的目录名进行下一次搜索 strcpy(dirNew, dir); strcat(dirNew, "\\"); strcat(dirNew, FileInfo.name); listFiles(dirNew); } else std::cout << FileInfo.name << "\t" << "\n"; } while (_findnext(handle, &FileInfo) == 0); //_findnext第一个参数为文件句柄,第二个参数同样为_finddata_t结构体指针。若查找成功,返回0,失败返回 - 1。 _findclose(handle); // 关闭搜索句柄 } std::string* listFiles(const char * dir,std::string* str) { char dirNew[200]; strcpy(dirNew, dir); int filenum = 0; std::string* temp = new std::string[50]; int partcount = 0; std::string filepath; char**filepathpart; filepathpart = (char**)malloc(sizeof(char) * 256); std::string filepathtemp = dir; intptr_t handle; //intptr_t是为了跨平台,其长度总是所在平台的位数,用来存放地址。 _finddata_t FileInfo; // _finddata_t 是用来存储文件各种信息的结构体。 handle = _findfirst(dirNew, &FileInfo); //第一个参数为文件名,可以用"*.*"来查找所有文件,也可以用"*.cpp"来查找.cpp文件。第二个参数是_finddata_t结构体指针。若查找成功,返回文件句柄,若失败,返回-1。 if (handle == -1) // 检查是否成功 return nullptr; filepathpart[partcount] = strtok((char*)filepathtemp.c_str(), "\\"); while (filepathpart[partcount] != NULL) { partcount++; filepathpart[partcount] = strtok(NULL, "\\"); } for (int i = 0; i < partcount - 1; i++) { filepath += filepathpart[i] ; filepath += "\\"; } do { if (FileInfo.attrib & _A_SUBDIR) //如果文件是目录 { if (strcmp(FileInfo.name, ".") == 0 || strcmp(FileInfo.name, "..") == 0) //如果文件名和当前目录和父目录相同则跳过,否则会只输出当前目录 continue; std::cout << FileInfo.name << "\t<目录>\n"; // 在目录后面加上"\\"和搜索到的目录名进行下一次搜索 strcpy(dirNew, dir); strcat(dirNew, "\\"); strcat(dirNew, FileInfo.name); listFiles(dirNew); } else { temp[filenum] = filepath; temp[filenum] += FileInfo.name; filenum++; std::cout << FileInfo.name << "\t" << " \n"; } } while (_findnext(handle, &FileInfo) == 0); //_findnext第一个参数为文件句柄,第二个参数同样为_finddata_t结构体指针。若查找成功,返回0,失败返回 - 1。 _findclose(handle); // 关闭搜索句柄 return temp; }
测试运行
测试-c,-w,-l,-a
测试文件:1.cpp
测试结果:
字符数为:30
单词数为:10
行数为:13
空行数为:1
注释行数为:3
代码行数为:9
1.cpp
113.cpp
13.cpp
2.cpp
555 <目录>
main.cpp
wcclass.h
113.cpp
13.cpp
2.cpp
main.cpp
C:\Users\L\Desktop\软件工程\test\1.cpp
字符数为:30
单词数为:20
行数为:39
C:\Users\L\Desktop\软件工程\test\113.cpp
字符数为:104
单词数为:52
行数为:75
C:\Users\L\Desktop\软件工程\test\13.cpp
字符数为:146
单词数为:85
行数为:111
C:\Users\L\Desktop\软件工程\test\2.cpp
字符数为:185
单词数为:114
行数为:141
C:\Users\L\Desktop\软件工程\test\main.cpp
字符数为:588
单词数为:581
行数为:417
总单词数为:810
总行数为:417
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
30 |
45 |
· Estimate |
· 估计这个任务需要多少时间 |
10 |
10 |
Development |
开发 |
100 |
300 |
· Analysis |
· 需求分析 (包括学习新技术) |
60 |
30 |
· Design Spec |
· 生成设计文档 |
20 |
20 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
20 |
100 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
20 |
10 |
· Design |
· 具体设计 |
10 |
10 |
· Coding |
· 具体编码 |
10 |
10 |
· Code Review |
· 代码复审 |
10 |
100 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
30 |
200 |
Reporting |
报告 |
10 |
30 |
· Test Report |
· 测试报告 |
10 |
30 |
· Size Measurement |
· 计算工作量 |
10 |
10 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
30 |
20 |
合计 |
380 |
925 |