软工个人项目 ——wc.exe
Posted Techro
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了软工个人项目 ——wc.exe相关的知识,希望对你有一定的参考价值。
1.GitHub项目地址
https://github.com/k8kiw/WordCount
2.PSP预计时间
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
20 |
|
· Estimate |
· 估计这个任务需要多少时间 |
20 |
|
Development |
开发 |
580 |
|
· Analysis |
· 需求分析 (包括学习新技术) |
120 |
|
· Design Spec |
· 生成设计文档 |
30 |
|
· Design Review |
· 设计复审 (和同事审核设计文档) |
0 |
|
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
20 |
|
· Design |
· 具体设计 |
60 |
|
· Coding |
· 具体编码 |
250 |
|
· Code Review |
· 代码复审 |
40 |
|
· Test |
· 测试(自我测试,修改代码,提交修改) |
60 |
|
Reporting |
报告 |
110 |
|
· Test Report |
· 测试报告 |
60 |
|
· Size Measurement |
· 计算工作量 |
20 |
|
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
30 |
|
合计 |
|
710 |
|
3.解题思路
编程语言
只会C++,没得选择,虽然C++需要很大工作量那也比C容易。
需求分析
考虑到是命令行程序,main函数里需要带参数之后再根据情况进行选择即可,根据功能可以大体分为三部分:处理参数、文件计数的实现、GUI界面。
用户输入参数后进行处理,然后根据情况选择什么计数,GUI界面也需要调用计数功能。
可能遇到的问题
文件的IO我不常用所以只记得大概用法,需要实现的时候要当场查询。GUI界面需要要用到Qt的库,而之前并未使用过VS来开发Qt项目,需要一定时间的学习成本以及可能会遇到各种未知的问题。带通配符的文件搜索如果不自己实现的话就会使用到Windows.h,这个之前基本没用过,也需要查阅资料。
4.设计实现过程
1.WordCount
根据传入的文件路径进行计数,所以成员变量需要有路径以及读文件流,然后根据不同的计数功能将其分为几个成员函数分别实现即可。
故主要的方法有:
int CountCharacters(); //计算字符
int CountWords(); //计算词数
int CountLines(); //计算行数
int CountDetails(); //计算详细行
2.MainWindow
主窗口,将GUI界面进行实现;成员变量主要有窗体内的各种控件以及布局管理器,以及设计GUI界面的函数。
除去界面设计,需要主要实现的函数为:
private slots: void on_button_clicked() //响应按键的槽函数
private: void OpenFile() //打开文件并进行计数
3.main函数
处理参数的部分本应放在一个类中,可当时并没有这么做,写到了主函数中导致其很混乱,下次应当注意。
主要的函数有:
void CountFile(string option, string path) //对文件计数
void SearchFile(string option, string path)//通配符搜索文件
int main(int argc, char *argv[]) //主函数,对传入的参数进行了分类并调用对应功能函数
而由于C/C++的局限性(char仅为1字节),在非英文的系统下对外部文件操作是十分不方便的,故windows下的搜索函数使用了宽字符数组wchar_t*来代替;
但由于我并未考虑使用宽字符串wstring(对应wchar_t字符串),所以还需要两个在string和wstring之间进行转换的函数:
char* wideCharToMultiByte(wchar_t* pWCStrKey) //wchar_t* 转 char*(直接赋值给string)
LPCWSTR stringToLPCWSTR(std::string orig) //string 转 wchar_t*
5.部分关键代码说明
1.main函数,处理传入的参数,根据参数个数分情况处理即可。
1 int main(int argc, char *argv[]) 2 { 3 if (argc < 2) 4 { 5 cerr << "请输入参数!" << endl; 6 } 7 else if (argc == 2) 8 { 9 string option = argv[1]; 10 //打开gui 11 if (option == "-x") 12 { 13 QApplication a(argc, argv); 14 MainWindow w; 15 w.show(); 16 cout << "GUI界面已打开" << endl; 17 return a.exec(); 18 } 19 else 20 cerr << "请输入路径!" << endl; 21 } 22 else //argc > 2 23 { 24 string option = argv[1]; //选项 25 string path = argv[2]; //路径 26 27 bool flag = false; //标志有无 -s 28 if (option == "-s" || path == "-s") 29 { 30 //参数不完整 31 if (argc == 3) 32 { 33 cerr << "请输入完整的参数!(如: -s -a *.c)" << endl; 34 system("pause"); 35 return 0; 36 } 37 //-s在前 38 if (option == "-s") 39 option = argv[2]; 40 41 path = argv[3]; //路径总是最后一个参数 42 flag = true; //有 -s 43 } 44 45 if (flag == false) 46 { 47 CountFile(option, path); 48 } 49 else //有通配符 50 { 51 SearchFile(option, path); 52 } 53 } 54 55 system("pause"); 56 return 0; 57 }
2.CountFile函数,同样是简单的分情况处理;需要注意的是,如果找不到该文件或者无法打开时构造函数会抛出异常(不抛出会卡死),此处进行处理。
1 void CountFile(string option, string path) 2 { 3 try 4 { 5 WordCount *wc = new WordCount(path.c_str()); //生成对象 6 7 if (option == "-c") //计算字符 8 { 9 cout << "文件" + path + "的字符数为:" << wc->CountCharacters() << endl << endl; 10 } 11 else if (option == "-w") //计算单词 12 { 13 cout << "文件" + path + "的单词数为:" << wc->CountWords() << endl << endl; 14 } 15 else if (option == "-l") //计算行数 16 { 17 cout << "文件" + path + "的行数为:" << wc->CountLines() << endl << endl; 18 } 19 else if (option == "-a") //详细行数 20 { 21 int lines = wc->CountDetails(); 22 cout << "文件" + path + "的总行数为:" << lines << endl << endl; 23 } 24 else 25 { 26 cout << "没有" + option + "选项!请重试" << endl; 27 } 28 29 delete wc; 30 } 31 catch (int) //处理异常 即没有成功打开文件导致的卡死 32 { 33 cerr << "打开文件失败" << endl; 34 } 35 36 }
3.SearchFile函数,用windows下的FindFirstFile和FindNextFile搜索即可实现
1 void SearchFile(string option, string path) 2 { 3 WIN32_FIND_DATA pFileData; 4 5 //搜索第一个文件 6 HANDLE hFile = FindFirstFile(stringToLPCWSTR(path), &pFileData); 7 if (hFile == INVALID_HANDLE_VALUE) 8 cout << "查找失败" << endl; 9 else 10 CountFile(option, wideCharToMultiByte(pFileData.cFileName)); 11 12 //剩下的文件 13 while (FindNextFile(hFile, &pFileData)) 14 { 15 //过滤 16 if (pFileData.cFileName[0] == \'.\' || pFileData.cFileName[0] == \'..\') 17 continue; 18 19 //递归扫描子目录 20 if (pFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 21 { 22 //文件名转string 23 string fileName = pFileData.cFileName; 24 //取得子目录路径 25 string nextPath = path; 26 nextPath += "\\\\"; 27 nextPath += fileName; 28 //递归 29 SearchFile(option, nextPath); 30 } 31 else 32 CountFile(option, wideCharToMultiByte(pFileData.cFileName)); 33 } 34 }
4.WordCount类实现
1 int WordCount::CountCharacters() 2 { 3 int count = 0; 4 5 char c; 6 while (!m_File.eof()) //遍历 7 { 8 //每读取一个字符就计数一次 9 m_File >> c; 10 if (isgraph(c)) 11 { 12 count++; 13 c = \'\\0\'; 14 } 15 } 16 17 //回到文件头,准备下一次使用 18 m_File.clear(); 19 m_File.seekg(0); 20 return count; 21 } 22 23 int WordCount::CountWords() 24 { 25 int count = 0; 26 27 string str; 28 while (!m_File.eof()) //遍历 29 { 30 //每读取一个单词就计数一次 31 m_File >> str; //iostream的运算符默认忽略空格 32 if (regex_search(str, regex("[a-zA-Z]+\\\\b"))) //正则表达式匹配 33 { 34 count++; 35 } 36 } 37 38 //回到文件头,防止下一次调用的时候无法读取该文件 39 m_File.clear(); 40 m_File.seekg(0); 41 return count; 42 } 43 44 int WordCount::CountLines() 45 { 46 int count = 0; 47 48 string str; 49 while (!m_File.eof()) //遍历 50 { 51 //每读取一行就计数一次 52 getline(m_File, str); 53 count++; 54 } 55 56 //回到文件头,防止下一次调用的时候无法读取该文件 57 m_File.clear(); 58 m_File.seekg(0); 59 return count; 60 } 61 62 int WordCount::CountDetails() 63 { 64 string str; //用于保存提取出的行 65 66 int count = 0; //总行数 67 68 while (!m_File.eof()) //遍历文件 69 { 70 //读取一行进行分析 71 getline(m_File, str); 72 count++; 73 74 //遍历string 75 string::iterator ite; 76 int flag = 0; //标记 77 for (ite = str.begin(); ite != str.end(); ite++) 78 { 79 //空白字符 or 仅含有{ --> 空行 80 //flag = 0 flag = 1 81 //否则为字符行 若为//..... --> 注释行 82 // 否则为代码行 83 if (flag == 0 && isspace(*ite)) //一直都是空白 84 { 85 flag = 0; 86 } 87 else if (flag == 0 && isgraph(*ite)) //遇到第一个字符 88 { 89 if ((*ite) == \'{\' || (*ite) == \'}\') //大括号标记为1否则为代码行 90 flag = 1; 91 else if ((*ite) == \'/\') 92 flag = 3; 93 else 94 flag = 2; 95 } 96 else if (flag == 1 && isspace(*ite)) //该字符后都为空白 97 { 98 flag = 1; 99 } 100 else if (flag == 1 && isgraph(*ite)) //第二个字符 101 { 102 //如果是注释行 无论有没有大括号 第二个字符都为/ 103 if (*ite == \'/\') 104 flag = 3; 105 else 106 flag = 2; 107 } 108 109 } //for 110 111 //根据flag的情况进行计数 112 if (flag == 0 || flag == 1) 113 blankCount++; 114 else if (flag == 2) 115 codeCount++; 116 else 117 commentaryCount++; 118 } //while 119 120 //计数完毕输出 121 cout << "文件" << m_Path << "的空白行数:" << blankCount << endl; 122 cout << "文件" << m_Path << "的代码行数:" << codeCount << endl; 123 cout << "文件" << m_Path << "的注释行数:" << commentaryCount << endl; 124 125 //回到文件头,防止下一次调用的时候无法读取该文件 126 m_File.clear(); 127 m_File.seekg(0); 128 return count; 129 }
5.MainWindow类的实现,GUI界面实现
1 MainWindow::MainWindow(QWidget *parent) 2 : QMainWindow(parent) 3 { 4 //初始化 5 InitWidgets(); 6 7 //设置标题 8 this->setWindowTitle("WordCount"); 9 this->resize(600, 400); 10 11 //设计布局 12 DesignLayout(); 13 14 //应用布局 15 centralWidget->setLayout(layout); 16 this->setCentralWidget(centralWidget); 17 } 18 19 MainWindow::~MainWindow() 20 { 21 22 } 23 24 void MainWindow::InitWidgets() 25 { 26 //初始化全部控件 27 button = new QPushButton(tr("选择文件")); 28 path = new QLabel(tr("文件路径:")); 29 30 charactersResult = new QLabel(tr("字符数:")); 31 wordsResult = new QLabel(tr("单词数:")); 32 33 blankLinesResult = new QLabel(tr("空白行:")); 34 codeLinesResult = new QLabel("代码行:"); 35 commentLinesResult = new QLabel("注释行:"); 36 linesResult = new QLabel("总行数:"); 37 38 layout = new QVBoxLayout; 39 centralWidget = new QWidget; 40 41 //设置最小大小 42 button->setMinimumHeight(70); 43 button->resize(70, 120); 44 path->setMinimumHeight(30); 45 46 charactersResult->setMinimumHeight(30); 47 wordsResult->setMinimumHeight(30); 48 49 blankLinesResult->setMinimumHeight(30); 50 codeLinesResult->setMinimumHeight(30); 51 commentLinesResult->setMinimumHeight(30); 52 linesResult->setMinimumHeight(30); 53 54 //连接 55 connect(button, SIGNAL(clicked()), this, SLOT(on_button_clicked())); 56 } 57 58 void MainWindow::DesignLayout() 59 { 60 //添加按钮 61 layout->addWidget(button, 0, Qt::Alignment(4)); 62 63 //个人项目:实现wc.exe(Java)