软工个人项目 ——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)

个人项目Wc.exe(JAVA)

个人项目(wc.exe)java

WC.exe 个人项目

个人项目-WC

wc.exe个人项目