
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[];
WCclass * test=new WCclass();
for (int i = ; i < argc + ; i++)
{
temp = filepath;
filename = argv[i];
filename = temp + "\\" + filename;
test->fileoperate(filename, paramater);
}
detele test;
return ;
}
指令解析与文件操作函数()
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) * ); while (getline(fin, str))
{
int strcount = ;
rowcount(); if ((str.length() == ) || ((str.length() == )&& (str[strcount] == (char)"{")) || ((str.length() == ) && (str[strcount] == (char)"}")))
{
emptyrowcount();
continue;
} strs[strcount] = strtok((char*)str.c_str(), " "); while (strs[strcount] != NULL)
{
strcount++;
strs[strcount] = strtok(NULL, " ");
} for (int i = ; i < strcount; i++)
{
string temp = strs[i];
int continuectrl = ;//控制单词连续
int dqmoff = ;//是否是;,{,}
int flag = ;////是否相邻
for (int j = ; j < temp.length(); j++)
{
ch = temp[j];
if ((ch >= && ch <= ) || (ch >= && ch <= ) || (ch >= && ch <= ) || (ch >= && ch <= ))
{
charcount();
continuectrl = ;
//注释行//的情况
if (ch == )
{
if (j == )
{
flag = ;
}
else if (j == )
{
if (flag == )
{
flag = ;
noterowcount();
}
}
}
//注释行({//,}//,;//)的情况
if ((ch == ) || (ch == ) || (ch == ))
{
if (j == ) {
dqmoff = ;
continue;
}
}
if (dqmoff == )
{
if (ch == )
{
if (flag == )
{
flag = ;
noterowcount();
}
else flag = ;
}
else
{
dqmoff = ;
}
} }
else if ((ch >= && ch <= ) || (ch >= && ch <= ))
{
if (continuectrl == )continue;
continuectrl = ;
wordcount();
}
else if (ch >= ''&&ch <= '')
{
digitcount();
}
else
{
cnwordcount();
}
}
}
}
if (paramater == paramater1)std::cout << "字符数为:" << charnums + cnwordnums / << 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) * );
filegroup=listFiles(filename.c_str(), filegroup); //获取递归处理文件的文件名数组
for (int i = ; filegroup[i].length() != ; 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 / << 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[];
strcpy(dirNew, dir); intptr_t handle; //intptr_t是为了跨平台,其长度总是所在平台的位数,用来存放地址。
_finddata_t FileInfo; // _finddata_t 是用来存储文件各种信息的结构体。 handle = _findfirst(dirNew, &FileInfo); //第一个参数为文件名,可以用"*.*"来查找所有文件,也可以用"*.cpp"来查找.cpp文件。第二个参数是_finddata_t结构体指针。若查找成功,返回文件句柄,若失败,返回-1。
if (handle == -) // 检查是否成功
return; do
{
if (FileInfo.attrib & _A_SUBDIR) //如果文件是目录
{
if (strcmp(FileInfo.name, ".") == || strcmp(FileInfo.name, "..") == ) //如果文件名和当前目录和父目录相同则跳过,否则会只输出当前目录
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) == ); //_findnext第一个参数为文件句柄,第二个参数同样为_finddata_t结构体指针。若查找成功,返回0,失败返回 - 1。 _findclose(handle); // 关闭搜索句柄
} std::string* listFiles(const char * dir,std::string* str)
{
char dirNew[];
strcpy(dirNew, dir);
int filenum = ;
std::string* temp = new std::string[];
int partcount = ;
std::string filepath;
char**filepathpart;
filepathpart = (char**)malloc(sizeof(char) * );
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 == -) // 检查是否成功
return nullptr; filepathpart[partcount] = strtok((char*)filepathtemp.c_str(), "\\"); while (filepathpart[partcount] != NULL)
{
partcount++;
filepathpart[partcount] = strtok(NULL, "\\");
} for (int i = ; i < partcount - ; i++) {
filepath += filepathpart[i] ;
filepath += "\\";
} do
{
if (FileInfo.attrib & _A_SUBDIR) //如果文件是目录
{
if (strcmp(FileInfo.name, ".") == || strcmp(FileInfo.name, "..") == ) //如果文件名和当前目录和父目录相同则跳过,否则会只输出当前目录
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) == ); //_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 |