• 现代软件工程个人作业——词频统计(字符数、行数、单词数、高频单词和词组)


     现代软件工程课的第一次个人作业博主做的相当差劲,让我清楚地意识到自己与他人的差距。

    通过这篇博客博主将展示自己是如何走上事倍功半的歧路,认真分析错误原因,希望大家不要重蹈我的覆辙。



    首先让我们来看一下作业要求详细要求在邓宏平老师的博客:第一次个人作业——词频统计

          这次词频统计的主要功能有:

    1. 统计文件的字符数(只需要统计Ascii码,汉字不用考虑,换行符不用考虑,'\0'不用考虑)(ascii码大小在[32,126]之间

    2. 统计文件的单词总数

    3. 统计文件的总行数(任何字符构成的行,都需要统计)(不要只看换行符的数量,要小心最后一行没有换行符的情形)(空行算一行)

    4. 统计文件中各单词的出现次数,对给定文件夹及其递归子文件夹下的所有文件进行统计

    6. 统计两个单词(词组)在一起的频率,输出频率最高的前10个。

        注意:

    a) 空格,水平制表符,换行符,均算字符

    b) 单词的定义:至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写。

    英文字母:A-Z,a-z

    字母数字符号:A-Z,a-z,0-9

    分割符:空格,非字母数字符号

    例如:”file123”是一个单词,”123file”不是一个单词。file,File和FILE是同一个单词

    如果两个单词只有最后的数字结尾不同,则认为是同一个单词,例如,windows,windows95和windows7是同一个单词,iPhone4和IPhone5是同一个单词,但是,windows和windows32a是不同的单词,因为他们不是仅有数字结尾不同。输出按字典顺序,例如,windows95,windows98和windows2000同时出现时,输出windows2000。单词长度只需要考虑[4, 1024],超出此范围的不用统计。

    c)词组的定义:windows95 good, windows2000 good123,可以算是同一种词组。按照词典顺序输出。三词相同的情形,比如good123 good456 good789,根据定义,则是 good123 good123 这个词组出现了两次。

    good123 good456 good789这种情况,由于这三个单词与good123都是同一个词,最终统计结果是good123 good123这个词组出现了2次

    两个单词分属两行,也可以直接组成一个词组。统计词组,只看顺序上,是否相邻

    d) 输入文件名以命令行参数传入。需要遍历整个文件夹时,则要输入文件夹的路径。

    e) 输出文件result.txt

    characters: number

    words: number

    lines: number

    <word>: number

    <word>为文件中真实出现的单词大小写格式,例如,如果文件中只出现了File和file,程序不应当输出FILE,且<word>按字典顺序(基于ASCII)排列,上例中程序应该输出File: 2

    f) 根据命令行参数判断是否为目录

    g) 将所有文件中的词汇,进行统计,最终只输出一个整体的词频统计结果。

         评分标准

    1. 统计文件的字符数(1分)

    2. 统计文件的单词总数(1分)

    3. 统计文件的总行数(1分)

    4. 统计文件中各单词的出现次数(1分)

    5. 对给定文件夹及其递归子文件夹下的所有文件进行统计(2分)

    6. 统计两个单词(词组)在一起的频率,输出频率最高的前10个(2分)

    以上六个结果输出错误则对应子任务得-1分,全部输出正确则按运行时间确定排名(用时按升序前30%得满分8分,30%-70%得7.5分,后30%得7分)。

    7. 博客撰写(代码实现过程,性能分析、优化报告等)(2分)

    8. 在Linux系统下,进行性能分析,过程写到blog中(附加题,2分)

        完成时间:一周之内

    需求分析

    1.统计字符数和行数容易实现。

    2.统计单词总数:作业要求中对单词的定义,是4个英文字母开头,后跟零个或多个英文字母或数字,单词长度在[4,1024]之间。一般来说匹配一定格式的字符串都用正则表达式和迭代器来实现。

    3.统计统计文件中各单词的出现次数,并输出出现频率最高的10个:单词存放于容器中,没出现一个新单词需要查找它是不是已经存在了,如果存在的话单词频率 加一,否则将单词加入容器。如何实现判断单词相等和不等是重要的一点。将所有单词收集到容器后需要根据出现频率对单词进行排序,并输出频率最高的10个。

    4.输出出现频率最高的10个词组:相邻的两个单词组成一个词组,也需要查重和依频率排序。

    5.对给定文件夹及其递归子文件夹下的所有文件进行统计:判断是目录还是文件,如果是目录,需要获取目录下文件的名字再对文件进行处理。

    接下来博主就走上了错误的第一步选择了不熟悉的编程语言

    一般来说,要在短时间内完成复杂和难度较高的工程,应该选择你熟悉的编程语言。

           博主会 对C比较熟悉但是没有系统学过C++,希望通过这门课的实践可以把C++学起来。结果博主天真地看了一整天的Primer C++(╯°Д°)╯,第二天就急躁躁地开始编程了。相信很多人都知道这本经典有多么晦涩难懂,可想而知一天下来我能吸收多少知识。事实上,更多的时间应该用来进一步分析需求,有针对性地查找解决方法,综合功能和性能的考虑设计多个方案,在比较、测试过后筛选出合理的方案。对于这个作业我还是推荐用C++写的,只是日程这么紧张的情况下,时间应该多分配给需求分析和前期构建的工作,C++上面的新知识可以现查现学。

    结果,博主又走错了路:选择了不合理的数据结构

    数据结构是重中之重,要慎之又慎

           博主在没有事先了解数据量的情况下选择了vector作为容器,因为它有find函数和sort函数。这种偷懒的行为是极其不可取的。

    构建工作应该优先考虑需求,而不是你目前的编程水平或者工作量。

           事实上博主考虑过map,但是为什么没有选择它呢,原因主要是我一开始的思路是为单词和词组各定义一个类,在类中存放单词和频率,重构==运算符以便判断是否相等,另外如果相等的单词字典序先于目前这个单词,就修改目前这个单词,如果用map,单词做key,频率作value,可是map不支持修改key值,因为map会自动根据key排序,key是它排序的基础。然后博主就这么把它抛弃了。

            其实,可以建立两个map,以单词的简写作为共同的key,一个map的value是单词的完整形式,另一个map的value是单词的频率。(助教的思路)

            另外,C++11还支持unordered_map, 它以哈希表为基础,查找时间复杂度只有O(1),而且不会自动根据Key值进行排序,但是占的空间相对较大。不过,对map类进行按值排序,一般需要将map中的数据以pair的形式传递给顺序容器(如我选择的vector)再用sort进行排序。顺序容器可用的sort排序效率都非常高, vector使用的是快速排序。

            如果让博主再有一次机会,博主会选什么呢?答案是map。虽然将map中的数据转移到vector中也需要耗费较多地时间,但只需要操作一次,也就是O(n)的复杂度但是vector中的find用的是线性探查,每得到一个新单词都得查重,时间复杂度已经是O(n*n)了。后面博主自己写了hash查找函数,和实时申请内存结合起来就会非常复杂(时间限制博主没有实现(ಥ_ಥ))。综合来看还是用map或unordered map比较合理。


    接下来分享代码。

    先上第一版,使用了vector可用的stl函数find和sort.

    说明:1.这一版程序里面可能还有一些小bug和比较写得比较生涉的地方,欢迎大家指出。05.txt是一份用于测试的文本文件。

               2. 将文件里的所有内容读作一个字符串,用来统计字符总数和收集新单词、新词组。

               3.从str里面获取新单词是用正则表达式匹配的,具体在getNewExpr函数里。

               4.使用sort之前需要定义compare方式或者重构>、<运算符,博主采用前者。

               5.getAllFiles函数用于判断路径是目录还是文件,如果是目录的话获取所有文件名并放入一个string类的vector中。

               6.阅读源码建议先读类定义然后从main函数开始依照线程阅读。

    #include<iostream>
    #include<fstream>
    #include<string>
    #include<sstream>
    #include<vector>
    #include<algorithm>
    #include<cctype>
    #include<regex>
    #include<io.h>
    
    using namespace std;
    int begFlag = 1;
    
    typedef struct {
        unsigned int charNum;
        unsigned int lineNum;
        unsigned int wordNum;
    }amount;
    
    class word {
    
    private:
        string wordStr;
        unsigned int freq;
    public:
        word() = default;
        word(string str) {
            wordStr = str;
            freq = 1;
        }
    
        string getWordStr() {
            return wordStr;
        }
    
        unsigned int getFreq() {
            return freq;
        }
    
        void addFreq() {
            freq++;
        }
    
        void resetWordStr(string str) {
            if (str < wordStr) {
                wordStr = str;
            }
        }
    
        bool operator == (const word &obj) const {
            string word1 = this->wordStr, word2 = obj.wordStr;
            int i = word1.length() - 1;
            int j = word2.length() - 1;
            while (i >= 0)
            {
                if (word1[i] >= '0'&&word1[i] <= '9')
                    word1[i] = '\0';
                else break;
                i--;
            }
            while (j >= 0)
            {
                if (word2[j] >= '0'&&word2[j] <= '9')
                    word2[j] = '\0';
                else break;
                j--;
            }
            if (i == j) {
    
                for (int t = 0; t <= i; t++) {
                    if (word1[t] != word2[t] && abs(word1[t] - word2[t]) != 32)
                        return false;
                }
    
    
            }
            else return false;
            return true;
        }
    
        void printWord(ofstream &output) {
            output << wordStr << "\t" << freq << endl;
        }
    };
    
    
    class phrase {
    
    private:
        //string phrStr;
        unsigned int freq;
        word part1, part2;
    
    public:
        //lack a default constructor
    
        phrase(word part1, word part2) {
            this->part1 = part1;
            this->part2 = part2;
            //phrStr = str;
            freq = 1;
        }
        /*
        string getPhrStr() {
            return phrStr;
        }
        */
        word getPart1() {
            return part1;
        }
    
        word getPart2() {
            return part2;
        }
    
        unsigned int getFreq() {
            return freq;
        }
    
        void addFreq() {
            freq++;
        }
    
        void resetPhrase(phrase &obj) {
    
            //string objStr = obj.getPhrStr();
    
            word objPart1 = obj.getPart1();   
            word objPart2 = obj.getPart2();  
            // '||' is a short circuit operator
            if (objPart1.getWordStr() < this->part1.getWordStr() || objPart2.getWordStr() < this->part2.getWordStr()) {
                this->part1 = objPart1;
                this->part2 = objPart2;
            }
        }
    
        bool operator == (const phrase &obj) const {
    
            word objPart1 = obj.part1, objPart2 = obj.part2;
            return (part1 == objPart1 && part2 == objPart2);
        }
    
        void printPhrase(ofstream &output) {
            string word1 = part1.getWordStr(), word2 = part2.getWordStr();
            word1 < word2 ?
                output << word1 + " " + word2 << "\t" << freq << endl :
                output << word2 + " " + word1 << "\t" << freq << endl;
        }
    };
    
    
    bool wordCompare(word former, word latter) {
        return former.getFreq() > latter.getFreq();
    }
    
    bool phraseCompare(phrase former, phrase latter) {
        return former.getFreq() > latter.getFreq();
    }
    
    
    
    void examineNewWord(vector<word> &wvec, word &newWord) {
    
        vector<word>::iterator beg = wvec.begin(), end = wvec.end(), itr;
        itr = find(beg, end, newWord);    //is there any repition?
    
        if (itr != end) {                 // this word already exists in wvec
            itr->resetWordStr(newWord.getWordStr());
            itr->addFreq();
        }
        else {
            wvec.push_back(newWord);
        }
    }
    
    void examineNewPhr(vector<phrase> &pvec, phrase &newPhrase) {
    
        vector<phrase>::iterator beg = pvec.begin(), end = pvec.end(), itr;
        itr = find(beg, end, newPhrase);   ////is there any repition?
    
        if (itr != end) {
            itr->resetPhrase(newPhrase);
            itr->addFreq();
        }
        else {
            pvec.push_back(newPhrase);
        }
    }
    
    
    /* collect all expressions that match the definition of word in the parameter string */
    void getNewExpr(string &str, vector<word> &wvec, vector<phrase> &pvec, amount &result) {
    
        word newWord;
        string wordPattern("[[:alpha:]]{4}[[:alnum:]]{0,1020}");
        regex reg(wordPattern);
    
        //intermediate variables in generating a new phrase
        
        //string::size_type pos1, pos2;
        string newPhrStr = "\0";
        word part1("\0"), part2("\0");
        phrase newPhrase( part1, part2);
        /*
          collect a word in advance, then combine two words and the substring 
          between them into  a phrase
        */
        for (sregex_iterator it(str.begin(), str.end(), reg), end_it;
            it != end_it; it++) {
            
            result.wordNum++;
            newWord = word(it->str());
            examineNewWord(wvec, newWord);
    
            if (begFlag) {
                
                begFlag = 0;
                part1 = newWord;
            }
            else {
                
                part2 = newWord;
                newPhrase = phrase(part1, part2);
                examineNewPhr(pvec, newPhrase);
                                  //pos1 = pos2;
                part1 = part2;
            }
            
        }
    }
    
    /* calculate the amount of characters with ASCII code within [32,126]*/
    unsigned long getCharNum(string &str) {
    
        unsigned long charNum = 0;
        string::iterator end = str.end(), citr;
        for (citr = str.begin(); citr != end; citr++) {
            if (*citr >= 32 && *citr <= 126)
                charNum++;
        }
        return charNum;
    }
    
    
    /* calculate the number of lines in one file */
    unsigned long getLineNum(string filename) {
    
        ifstream input(filename);
        unsigned long lines = 0;
        string str;
        while (!input.eof()) {
    
            getline(input, str);
            lines++;
        }
    
        return lines;
    }
    
    
    
    /*
      process one file, update the amount of characters and the amount of lines, 
      collect all expressions that match the word definition into wvec.
    */
    void fileProcess(string filename, amount &result, vector<word> &wvec, vector<phrase> &pvec) {
    
        ifstream input;
        stringstream buffer;
        string srcStr;
    
        try {
            input.open(filename);
            if (!input.is_open()) {
                throw runtime_error("cannot open the file");
            }
        }
        catch (runtime_error err) {
            cout << err.what();
            return ;
        }
        
    
        if (input.eof())
            return;
    
            buffer << input.rdbuf();
            srcStr = buffer.str();
    
            // update the amount of characters
            result.charNum += getCharNum(srcStr);
    
            //update the amount of lines
            result.lineNum += getLineNum(filename);
    
            //update the wvec
            getNewExpr(srcStr, wvec, pvec,result);
        
    
        input.close();
    }
    
    
    /* print the results in the required format*/
    void getResult(const char* resfile, amount &result, vector<word> &wvec, vector<phrase> &pvec) {
    
        auto wvecSize = wvec.size();
        auto pvecSize = pvec.size();
        ofstream output(resfile);
    
        output << "char_number :" << result.charNum << endl;
        output << "line_number :" << result.lineNum << endl;
        output << "word_number :" << result.wordNum << endl;
    
        //sort wvec in descending frequency order
        vector<word>::iterator wbeg = wvec.begin(), wend = wvec.end(), witr;
        sort(wbeg, wend, wordCompare);
    
        output << " " << endl;
        output << "the top ten frequency of words" << endl;
        if(wvecSize){
    
            if (wvecSize < 10) {
                for (witr = wbeg; witr != wend; witr++) {
                    witr->printWord(output);
                }
            }
            else {
                vector<word>::iterator wlast = wbeg + 10;
                for (witr = wbeg; witr != wlast; witr++) {
                    witr->printWord(output);
                }
            }
        }
    
        //sort pvec in descending frequency order
        vector<phrase>::iterator pbeg = pvec.begin(), pend = pvec.end(), pitr;
        sort(pbeg, pend, phraseCompare);
    
        output << " " << endl;
        output << "the top ten frequency of phrases" << endl;
        if (pvecSize) {
    
            if (pvecSize < 10) {
                for (pitr = pbeg; pitr != pend; pitr++) {
                    pitr->printPhrase(output);
                }
            }
            else {
                vector<phrase>::iterator plast = pbeg + 10;
                for (pitr = pbeg; pitr != plast; pitr++) {
                    pitr->printPhrase(output);
                }
            }
        }
        
    }
    
    
    /* determine whether the given path is a directory or a file, 
       if it is a directory, push names of all the files in the 
       directory into fvec*/
    int getAllFiles(string path, vector<string> &files)
    { 
        long   hFile = 0;
        int flag = -1;
        
        struct _finddata_t fileinfo;  
        string p;  
    
        if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
        {
            flag = 0;
            while (_findnext(hFile, &fileinfo) == 0)
            {
                if ((fileinfo.attrib &  _A_SUBDIR))  //if it is a folder
                {
                    if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
                    {
                        //files.push_back(p.assign(path).append("/").append(fileinfo.name));//save filename
                        getAllFiles(p.assign(path).append("/").append(fileinfo.name), files);
                    }
                }
                else    //it is a file
                {
                    files.push_back(p.assign(path).append("/").append(fileinfo.name));//文件名
                }
            }  
            _findclose(hFile);
        }
    
        return flag;
    }
    
    
    int main(int argc, char* argv[]) {
    
        amount  result;
        result.charNum = 0;
        result.lineNum = 0;
        result.wordNum = 0;
        vector<word> wvec;
        vector<phrase> pvec;
        int dirFlag;
        vector<string> fvec;
    
        string path = "05.txt";
        
        const char* resFile = "AllFiles.txt";
    
        dirFlag = getAllFiles(path, fvec);
    
        if (dirFlag == 0) {
    
            vector<string>::iterator end = fvec.end(), it;
            for (it = fvec.begin(); it != end; it++) {
                fileProcess(*it, result, wvec, pvec);
            }
        }
        else {
            fileProcess(path, result, wvec, pvec);
        }
        
        getResult(resFile, result, wvec, pvec);
        system("pause");
    }

    输出结果:

    VS性能向导给出的结果:

    由此可以确定examineNewPhr里的查找过程find非常耗时间,因为find是根据这个重构的运算符来判断是否相等的。

    为此,博主决定在vector里面存放一个哈希表。

    第二版代码如下:

    说明:这一版只实现了单词统计没有实现词组统计。除了word类定义、examineNewWord函数被修改,增加了hash函数外,其他基本没太大的变化。newsample是我们这次作业的测试集,数据量大概175M字节。用上面第一版根本跑不起来。

    #include<iostream>
    #include<fstream>
    #include<string>
    #include<sstream>
    #include<vector>
    #include<algorithm>
    #include<cctype>
    #include<regex>
    #include<io.h>
    
    using namespace std;
    
    #define WORD_POOL_SIZE 18000000;
    #define MAX_FIGURES 20;
    int begFlag = 1;
    
    typedef struct {
        unsigned int charNum;
        unsigned int lineNum;
        unsigned int wordNum;
    }amount;
    
    class word{
    
    public:
        string wordStr;
        unsigned int freq;
    
        word(string str, unsigned int fre) {
            wordStr = str;
            freq = fre;
        }
    
    
        void resetWordStr(string str) {
            if (str < wordStr) {
                wordStr = str;
            }
        }
    
        bool operator == (const word &obj) const {
            string word1 = this->wordStr, word2 = obj.wordStr;
            int i = word1.length() - 1;
            int j = word2.length() - 1;
            while (i >= 0)
            {
                if (word1[i] >= '0'&&word1[i] <= '9')
                    word1[i] = '\0';
                else break;
                i--;
            }
            while (j >= 0)
            {
                if (word2[j] >= '0'&&word2[j] <= '9')
                    word2[j] = '\0';
                else break;
                j--;
            }
            if (i == j) {
    
                for (int t = 0; t <= i; t++) {
                    if (word1[t] != word2[t] && abs(word1[t] - word2[t]) != 32)
                        return false;
                }
    
            }
            else return false;
            return true;
        }
    
        void printWord(ofstream &output) {
            output << wordStr << "\t" << freq << endl;
        }
        
    };
    
    bool wordCompare(word former, word latter) {
        return former.freq > latter.freq;
    }
    
    unsigned int Hash(string str) {
        
        const char *p = str.c_str();
        unsigned int seed = 7, key;
        unsigned long long hash = 0;
        int figures=0;
        while (*p!='\0'&& figures<= 20) {
            hash = hash*seed + (*p);
            p++;
            figures++;
        }
        key = hash%WORD_POOL_SIZE;
        return(key);
    }
    
    void examineNewWord(vector<word> &wvec, word &newWord) {
    
        string str = newWord.wordStr;
        int i = str.length() - 1;
    
        while (i >= 0) {
            if (str[i] >= '0'&&str[i] <= '9') {
                str[i] = '\0';
            }
            else if (str[i]>=97&&str[i]<=122) {
                str[i] = str[i] - 32;
            }
            i--;
        }
    
        unsigned int key = Hash(str);
        int outOfSlot = 1;
        int open = 0;
        vector<word>::iterator beg = wvec.begin();
        vector<word>::iterator itr = beg + key;
        
        while (outOfSlot) {
            itr = beg + (itr - beg + open * 13)%WORD_POOL_SIZE;
            if (itr->wordStr == "\0") {
                itr->wordStr = newWord.wordStr;
                itr->freq++;
                outOfSlot = 0;
            }
            else if (*itr == newWord) {
                itr->resetWordStr(newWord.wordStr);
                itr->freq++;
                outOfSlot = 0;
            }
            open++;
        }
    
    }
    
    void getNewExpr(string &str, vector<word> &wvec, unsigned int &wordNum) {
    
        word newWord("\0",1);
        string wordPattern("[[:alpha:]]{4}[[:alnum:]]{0,1020}");
        regex reg(wordPattern);
    
        for (sregex_iterator it(str.begin(), str.end(), reg), end_it;
            it != end_it; it++) {
            wordNum++;
            newWord.wordStr = it->str();
            examineNewWord(wvec, newWord);
        }
    }
    
    /* calculate the amount of characters with ASCII code within [32,126]*/
    unsigned long getCharNum(string &str) {
    
        unsigned long charNum = 0;
        string::iterator end = str.end(), citr;
        for (citr = str.begin(); citr != end; citr++) {
            if (*citr >= 32 && *citr <= 126)
                charNum++;
        }
        return charNum;
    }
    
    
    /* calculate the number of lines in one file */
    unsigned long getLineNum(string filename) {
    
        ifstream input(filename);
        unsigned long lines = 0;
        string str;
        while (!input.eof()) {
            /*
            if (getline(input, str)) {
    
                lines++;
            }*/
            getline(input, str);
            lines++;
        }
    
        return lines;
    }
    
    void fileProcess(const char* filename, amount &result, vector<word> &wvec) {
    
        ifstream input;
        stringstream buffer;
        string srcStr;
    
        try {
            input.open(filename);
            if (!input.is_open()) {
                throw runtime_error("cannot open the file");
            }
        }
        catch (runtime_error err) {
            cout << err.what();
            return;
        }
    
    
        if (input.eof())
            return;
    
        buffer << input.rdbuf();
        srcStr = buffer.str();
    
        // update the amount of characters
        result.charNum += getCharNum(srcStr);
    
        //update the amount of lines
        result.lineNum += getLineNum(filename);
    
        //update the wvec
        getNewExpr(srcStr, wvec, result.wordNum);
    
    
        input.close();
    }
    
    /* print the results in the required format*/
    void getResult(const char* resfile, amount &result, vector<word> &wvec) {
    
        auto wvecSize = wvec.size();
        //auto pvecSize = pvec.size();
        ofstream output(resfile);
    
        output << "char_number :" << result.charNum << endl;
        output << "line_number :" << result.lineNum << endl;
        output << "word_number :" << result.wordNum << endl;
    
        //sort wvec in descending frequency order
        vector<word>::iterator wbeg = wvec.begin(), wend = wvec.end(), witr;
        sort(wbeg, wend, wordCompare);
    
        output << " " << endl;
        output << "the top ten frequency of words" << endl;
        if (wvecSize) {
    
            if (wvecSize < 10) {
                for (witr = wbeg; witr != wend; witr++) {
                    witr->printWord(output);
                }
            }
            else {
                vector<word>::iterator wlast = wbeg + 10;
                for (witr = wbeg; witr != wlast; witr++) {
                    witr->printWord(output);
                }
            }
        }
        
    }
    
    /* determine whether the given path is a directory or a file,
    if it is a directory, push names of all the files in the
    directory into fvec*/
    int getAllFiles(string path, vector<string> &files)
    {
        long   hFile = 0;
        int flag = -1;
    
        struct _finddata_t fileinfo;
        string p;
    
        if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
        {
            flag = 0;
            while (_findnext(hFile, &fileinfo) == 0)
            {
                if ((fileinfo.attrib &  _A_SUBDIR))  //if it is a folder
                {
                    if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
                    {
                        //files.push_back(p.assign(path).append("/").append(fileinfo.name));//save filename
                        getAllFiles(p.assign(path).append("/").append(fileinfo.name), files);
                    }
                }
                else    //it is a file
                {
                    files.push_back(p.assign(path).append("/").append(fileinfo.name));//文件名
                }
            }
            _findclose(hFile);
        }
    
        return flag;
    }
    
    int main(int argc, char* argv[]) {
    
        amount  result;
        result.charNum = 0;
        result.lineNum = 0;
        result.wordNum = 0;
        vector<word> wvec(18000000,word("\0",0));
        int dirFlag;
        vector<string> fvec;
    
        const char* path = "D:/Visual Studio/newsample";
    
        const char* resFile = "AllFiles.txt";
    
        dirFlag = getAllFiles(path, fvec);
    
        if (dirFlag == 0) {
    
            vector<string>::iterator end = fvec.end(), it;
            for (it = fvec.begin(); it != end; it++) {
                fileProcess(it->c_str(), result, wvec);
            }
        }
        else {
            fileProcess(path, result, wvec);
        }
    
        getResult(resFile, result, wvec);
        //system("pause");
    }

    在来看看VS给出的性能向导报告:

           显然查找的效率变高了很多,新的冤大头转移到到了正则表达式匹配上面。

           这个问题如何优化,博主暂时还没有进行调查。另外,这一版程序有一个突出的问题就是空间的浪费,即在main函数中直接开size为18000000的vector,这种做法对栈的占用率非常高,由于词组的数目至少是单词的两倍,就需要把vector的空间开到35000000(因为实际结果是33000000多),会导致Stack Overflow问题。这说明我的哈希冲突解决策略不合理,应该选择可以动态申请内存、对空间利用比较合理的冲突解决方法。


           看到这里,你大概能理解博主后悔的心情。要说错误的起始点在哪里,那还是前期需求分析和构建工作做的太仓促了。应老师要求,我们使用Teambition制作PSP(Personal Software Process (PSP, 个人开发流程,或称个体软件过程)。但是博主找不到导出的功能键在哪里,(ಠ_ಠ),所以先上截图再临时做个表格吧

    任务 预计完成时间 实际用时
    学习C++基础知识 6h 10h
    为各项功能实现解决方案 6h 8h
    设计程序总架构 30min 25min
    实现字符总数、行数、单词总数、单词出现次数的统计    
    1h 2.5h
    实现统计词组出现次数,输出最高10个 2h 2h
    实现对文件夹内所有文件进行统计 1h 1.5h
    和标准结果进行对比,优化代码 12h

     

    博主该去补一补其他科的作业了......

    之后有时间的话会更新班里面词频统计做的比较优秀的方案。Σ(・ω・ノ)ノ

  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    程序员,你最重要的选择是和谁结婚,你最重要的能力是赚钱,钱和女友两手抓...
    计算机网络基础 — Linux 流量控制
    Oracle 20c 新特性:持久化内存数据库
    数据库每日一题 2020.04.30
  • 原文地址:https://www.cnblogs.com/VIAROW/p/8670270.html
Copyright © 2020-2023  润新知