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 //添加文字
64 layout->addWidget(path, 0, Qt::Alignment(4));
65 layout->addWidget(charactersResult, 0, Qt::Alignment(4));
66 layout->addWidget(wordsResult, 0, Qt::Alignment(4));
67
68 layout->addWidget(blankLinesResult, 0, Qt::Alignment(4));
69 layout->addWidget(codeLinesResult, 0, Qt::Alignment(4));
70 layout->addWidget(commentLinesResult, 0, Qt::Alignment(4));
71 layout->addWidget(linesResult, 0, Qt::Alignment(4));
72 }
73
74 void MainWindow::OpenFile()
75 {
76 //打开资源管理器并获取选择的路径
77 QString strPath = QFileDialog::getOpenFileName(this,
78 "选择文件", ".", tr("Text(*.txt *.c *.cpp *.java)"));
79
80 //检查路径
81 if (!strPath.isEmpty())
82 {
83 //显示路径
84 path->setText("文件路径:" + strPath);
85
86 //实例化对象
87 WordCount *wc = new WordCount(strPath.toStdString());
88
89 //输出结果
90 charactersResult->setText("字符数:" + QString::number(wc->CountCharacters()));
91 wordsResult->setText("单词数:" + QString::number(wc->CountWords()));
92
93 //输出
94 linesResult->setText("总行数:" + QString::number(wc->CountDetails()));
95 blankLinesResult->setText("空白行:" + QString::number(wc->blankCount));
96 codeLinesResult->setText("代码行:" + QString::number(wc->codeCount));
97 commentLinesResult->setText("注释行:" + QString::number(wc->commentaryCount));
98
99 delete wc;
100 }
101 }
102
103 void MainWindow::on_button_clicked()
104 {
105 OpenFile();
106 }

6.测试运行

由于使用了Qt的库,直接在命令行下运行exe文件需要导入大量dll文件及其不方便,所幸vs提供了带参数调试的功能,故测试依旧在IDE中完成。

测试文件如下:

    

    

单参数时如下,参数不再做演示

   

带通配符   

GUI界面

点击选择文件后调用资源管理器

执行结果

7.PSP实际时间

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

 20

20

· Estimate

· 估计这个任务需要多少时间

20

20

Development

开发

580

680

· Analysis

· 需求分析 (包括学习新技术)

120

150

· Design Spec

· 生成设计文档

30

30

· Design Review

· 设计复审 (和同事审核设计文档)

0

0

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

20

20

· Design

· 具体设计

60

50

· Coding

· 具体编码

250

320

· Code Review

· 代码复审

40

30

· Test

· 测试(自我测试,修改代码,提交修改)

60

80

Reporting

报告

110

120

· Test Report

· 测试报告

60

70

· Size Measurement

· 计算工作量

20

10

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

30

40

合计

710

820

8.项目小结

1.项目的前期准备与计划十分重要,例如要规划使用几个类、每个类中大致有什么功能、要使用什么库以及在此IDE下会不会有兼容性问题等都需要提前规划好 ,有了一个较为清晰的规划之后在实际开发时能够做到条理比较清晰,否则会在后续开发中走很多的弯路而且改动起来工作量很大。当然,开发完成后的各种测试、复审以及小结都是必不可少的。

2.前期的规划并不充分导致了在实际开发中走了很多弯路,导致了实际开发比预估时间长太多,而且代码中也存在着很多不足的地方需要改进。

3.基础还不够扎实,很多东西需要当场查阅,需要继续努力。

软工个人项目 ——wc.exe的更多相关文章

  1. 软工个人项目———WC.exe(Java实现)

    一.github地址 https://github.com/hhw-15521301615/hello-world 二.PSP表格 PSP2.1 Personal Software Process S ...

  2. 2020BUAA软工结伴项目作业

    2020BUAA软工结伴项目作业 17373010 杜博玮 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 结伴项目作业 我在这个课程的目标是 学 ...

  3. [2017BUAA软工]个人项目

    软工个人项目 一.Github项目地址 https://github.com/Lydia-yang/2017BUAA-SoftwareEngineering 二.解题思路 在刚开始拿到题目的时候,关于 ...

  4. [2017BUAA软工]结对项目

    软工结对项目 一. Github项目地址 https://github.com/crvz6182/sudoku_partner 二. PSP表格 Psp personal software progr ...

  5. 软工团队项目之团队展示&选题(OnTime——S.L.N)

    软工团队项目之团队展示&选题(OnTime——S.L.N) 一.团队展示 队名:『S.L.N』即Seigelion——乃“攻城狮”之意. 队员学号: 团队项目描述:(项目名称:OnTime) ...

  6. 2020BUAA软工个人项目作业

    2020BUAA软工个人项目作业 17373010 杜博玮 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人项目作业 我在这个课程的目标是 学 ...

  7. 软工个人项目(Java实现)

    一. Github地址: https://github.com/RuiBingo/PersonalWork 二.个人PSP表格: PSP2.1 PSP阶段 预估耗时(分钟) 实际耗时(分钟) Plan ...

  8. 个人项目 wc.exe

    GitHub地址:https://github.com/oAiuo/wordCount 一.题目描述 Word Count1. 实现一个简单而完整的软件工具(源程序特征统计程序).2. 进行单元测试. ...

  9. 个人项目WC.exe Node.js+electron实现

    前言 实现语言:Javascript 编译工具:webstorm GitHub:https://github.com/NPjuan/WC.git 项目要求 wc.exe 是一个常见的工具,它能统计文本 ...

随机推荐

  1. dp背包 面试题 08.11. 硬币

    https://leetcode-cn.com/problems/coin-lcci/ 硬币.给定数量不限的硬币,币值为25分.10分.5分和1分,编写代码计算n分有几种表示法.(结果可能会很大,你需 ...

  2. Linux踩坑之云服务器 ssh 连接不上

    前奏:今天没事处理一下之前远程不了Linux桌面的问题时,找到一个解决方法(开始入坑):                     systemctl set-default graphical.tar ...

  3. 剑指offer刷题(算法类_2)

    排序 035-数组中的逆序对(归并排序) 题目描述 题解 代码 复杂度 029-最小的K个数(堆排序) 题目描述 题解 代码 复杂度 029-最小的K个数(快速排序) 题目描述 题解 代码 复杂度 位 ...

  4. JavaScript高级程序设计(第四版) -- 随笔 -- 数组(未完)

    数组方法 .every() 与 .some() 传给两个个方法的函数都接收3个参数:数组元素.元素索引和数组本身. .every() -- 对于每一项都需要返回true,它才会返回true 若中途有一 ...

  5. C#设计模式-原型模式(Prototype Pattern)

    引言 在软件开发过程中,我们习惯使用new来创建对象.但是当我们创建一个实例的过程很昂贵或者很复杂,并且需要创建多个这样的类的实例时.如果仍然用new操作符去创建这样的类的实例,会导致内存中多分配一个 ...

  6. 网络发布工具 Apache/Nginx

    四大主流发布服务器 注:发布服务器的背后都是socket套接字 1.Apache阿帕奇 - 多进程 2.IIS -多线程 3.Nginx (engine x)(新) -支持异步IO,是现在最快的发布服 ...

  7. Linux(CentOS6.8)配置Redis

    1.Redis简介 Redis:REmote DIctionary Server(远程字典服务器). Redis是完全开源免费的,用C语言编写的,遵守BSD协议,是一个高性能的(key/value)分 ...

  8. 5G时代,URL Rewrite 还吃香吗

    URL Rewrite是网站建设中经常用到的一项技巧,通过 rewrite 我们能够屏蔽服务器运行态的信息,包括服务的程序.参数等等,给用户呈现美化后的URL,同时对搜索引擎更加友好,方便我们网站的推 ...

  9. 思维导图MindManager的过滤主题功能如何使用

    MindManager是一款多功能思维导图工具软件.但有的思维导图繁杂,用户只需要查看自己感兴趣的主题该怎么办呢?接下来,我就为大家详细介绍MindManager思维导图2020版的过滤主题功能,可以 ...

  10. 教你用Camtasia制作精美片头

    大家都知道在视频播放中,如果有一个令人印象深刻的精彩开头,整个视频的内容都能因此得到不少升华.所以有一个好的片头对于视频的制作来说十分重要.今天我们就来讲一下用Camtasia制作片头的方法. 首先, ...