码迷,mamicode.com
首页 > 其他好文 > 详细

软工个人项目 ——wc.exe

时间:2020-03-14 13:00:29      阅读:65      评论:0      收藏:0      [点我收藏+]

标签:next   tla   根据   ext   echart   minimum   布局管理器   局限性   注意   

1.GitHub项目地址

https://github.com/k8kiw/WordCount

2.PSP预计时间

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

20

 

· Estimate

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

 20

 

Development

开发

 500

 

· Analysis

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

 100

 

· Design Spec

· 生成设计文档

 30

 

· Design Review

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

 0

 

· Coding Standard

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

 20

 

· Design

· 具体设计

 50

 

· Coding

· 具体编码

 80

 

· Code Review

· 代码复审

40

 

· Test

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

 60

 

Reporting

报告

 60

 

· Test Report

· 测试报告

 50

 

· Size Measurement

· 计算工作量

 20

 

· Postmortem & Process Improvement Plan

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

 30

 

合计

 

 1080

 

 

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

 15

· Estimate

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

 20

 20

Development

开发

 500

 550

· Analysis

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

 100

 80

· Design Spec

· 生成设计文档

 30

 40

· Design Review

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

 0

 0

· Coding Standard

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

 20

 20

· Design

· 具体设计

 50

 70

· Coding

· 具体编码

 80

 130

· Code Review

· 代码复审

40

30

· Test

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

 60

 60

Reporting

报告

 60

 90

· Test Report

· 测试报告

 50

 40

· Size Measurement

· 计算工作量

 20

 30

· Postmortem & Process Improvement Plan

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

 30

 30

合计

 

 1080

 1205

8.项目小结

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

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

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

软工个人项目 ——wc.exe

标签:next   tla   根据   ext   echart   minimum   布局管理器   局限性   注意   

原文地址:https://www.cnblogs.com/KotoriQAQ/p/12466267.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!