zoukankan      html  css  js  c++  java
  • 词频分析项目报告

    • 要求
    1. 对源文件(*.txt,*.cpp,*.h,*.cs,*.html,*.js,*.java,*.py,*.php等)统计字符数、单词数、行数、词频,统计结果以指定格式输出到默认文件中,以及其他扩展功能,并能够快速地处理多个文件。
    2. 使用性能测试工具进行分析,找到性能的瓶颈并改进
    3. 对代码进行质量分析,消除所有警告
    4. 设计10个测试样例用于测试,确保程序正常运行(例如:空文件,只包含一个词的文件,只有一行的文件,典型文件等等)
    5. 使用Github进行代码管理
    6. 撰写博客

    基本功能

    1. 统计文件的字符数
    2. 统计文件的单词总数
    3. 统计文件的总行数
    4. 统计文件中各单词的出现次数
    5. 对给定文件夹及其递归子文件夹下的所有文件进行统计
    6. 统计两个单词(词组)在一起的频率,输出频率最高的前10个。

    功能模块

    • 遍历目录

                     调用函数listDir,参数path为需要读取的路径,变量childpath为文件名

    void listDir(char *path){
        DIR      *pDir=NULL;
        struct dirent  *ent=NULL;
        int       i=0;
        char      childpath[512],ch;
        
        pDir=opendir(path);
        memset(childpath,0,sizeof(childpath));
        
        while((ent=readdir(pDir))!=NULL){
            if(ent->d_type & DT_DIR){
                if(strcmp(ent->d_name,".")==0||strcmp(ent->d_name,"..")==0)continue;
                sprintf(childpath,"%s%s/",path,ent->d_name);
                listDir(childpath);
            }
            else{
                sprintf(childpath,"%s%s",path,ent->d_name);
                    
                puts(childpath);
    
                if((fin=fopen(childpath,"r"))==NULL){
                    fprintf(fout,"cannot open this file\n");
                    exit(0);
                }
            
            
                while((ch=fgetc(fin))!=EOF){
                    if(ch>=32&&ch<=126){         //counting the number of character
                        count_character++;  
                    }
                    if(ch=='\n'){
                        count_line++;
                    } 
                    wordjudge(ch);
                } 
            
                preword=NULL;
                wordstore[0]=wordstore[1]='\0'; 
                count_line++; 
                fclose(fin);
            }
        }
    }
    • 判断单词

                  调用函数wordjudge,对输入的字符判断其能否构成一个单词。若能,则将形成的字符进行下一步处理

    void wordjudge(char ch){
        int i;
        char dest[WORDLEN]={0};
        if(ch>='A'&&ch<='Z'||ch>='a'&&ch<='z'||ch>='0'&&ch<='9'){          
            i=wordstore[0];
            if(i<WORDLEN-2){
                i=++wordstore[0]; 
                wordstore[i]=ch;
                wordstore[i+1]='\0';
            }
            else{
            //    printf("the word is too long!\n");
            }
        }
        else if(wordstore[0]) {
            for(i=1;i<=4&&wordstore[i]&&(wordstore[i]>='A'&&wordstore[i]<='Z'||wordstore[i]>='a'&&wordstore[i]<='z');i++);
            if(i==5){
                strncpy(dest,wordstore+1,strlen(wordstore)-1);
                dest[strlen(dest)]='\0';
                wordoperation(dest);
                count_word++;
            }
            wordstore[0]=0;
            wordstore[1]='\0';
        }
    }
    • 单词处理

                     对要处理的单词进行标准化,对标准化后的单词哈希处理,存入单词结构体哈希表中。同时也顺带将当前单词和前一个单词组成的词组存入词组哈希表中

    void wordoperation(char* newword){
        int i,t,j,k,h=0,g=0;
        char c,newwordc[WORDLEN];
        strcpy(newwordc,newword);
        t=(int)strlen(newword);
        c=*(newwordc+t-1);
        for(;c>='0'&&c<='9';t--){
            newwordc[t]='\0';
            c=*(newwordc+t-1);
        }
        strlwr(newwordc);
        /*The following centences are to store word*/
        j=strlen(newwordc);
        for(k=1;k<j&&k<10;k++){
            h=(newwordc[k-1]+(h*36))%WORDNUM;
        }
        while(Hashw[h].frequency&&strcmp(Hashw[h].after_deal,newwordc)){
            h=(h+1)%WORDNUM;
        }
        if(Hashw[h].frequency){
            Hashw[h].frequency ++;
            if(strncmp(Hashw[h].content ,newword,51)>0){
                strcpy(Hashw[h].content  ,newword);
            }
        }
        else{
            Hashw[h].frequency =1;
            strcpy(Hashw[h].content,newword);
            strcpy(Hashw[h].after_deal ,newwordc); 
        }
        /*The following centences are to store word group*/
        if(preword){
            for(i=0;i<4;i++){
                g=(g*36+preword->after_deal[i])%GROUPNUM;
            }
            for(i=0;i<4;i++){
                g=(g*36+newwordc[i])%GROUPNUM; 
            }
            while(Hashg[g].frequency && 
            (strncmp(Hashg[g].firstword->after_deal,preword->after_deal,51)||strncmp(Hashg[g].secword->after_deal,newwordc,51))){
                g=(g+1)%GROUPNUM;
            }
            if(Hashg[g].frequency){
                Hashg[g].frequency ++;
            }
            else{
                Hashg[g].frequency =1;
                Hashg[g].firstword =preword;
                Hashg[g].secword =&Hashw[h];
            }
        
        }
        preword=&Hashw[h];
    }
    • 寻找前十单词和词组

                      遍历哈希表,找出前十单词和词组

    void toptenw(void){
        word tenw[10]={0};
        int j,m,n;
        for(j=0;j<WORDNUM;j++){
            if(Hashw[j].frequency ){
                for(m=9;m>=0&&(!tenw[m].frequency||Hashw[j].frequency>tenw[m].frequency);m--);
                if(m+1<=9){
                    for(n=9;n>m+1;n--){
                        tenw[n]=tenw[n-1];
                    }
                    tenw[m+1]=Hashw[j];
                }
            }
        }
        for(j=0;j<10;j++){
            if(tenw[j].frequency)fprintf(fout,"%s: %d\n",tenw[j].content,tenw[j].frequency);
        }
    }
    
    void topteng(void){
        wordgroup teng[10]={0};
        long int j,m,n;
        for(j=0;j<GROUPNUM;j++){
            if(Hashg[j].frequency ){
                for(m=9;m>=0&&(!teng[m].frequency||Hashg[j].frequency>teng[m].frequency);m--);
                if(m+1<=9){
                    for(n=9;n>m+1;n--){
                        teng[n]=teng[n-1];
                    }
                    teng[m+1]=Hashg[j];
                }
            }
        }
        for(j=0;j<10;j++){
            if(teng[j].frequency)fprintf(fout,"%s %s: %d\n",teng[j].firstword->content,teng[j].secword->content,teng[j].frequency);
        }
    }

    性能分析

                

    release模式下运行时间大概16s左右,可以看到:

    1. fgetc耗时量巨大,但是读字符是必不可少的操作所以不好修改。
    2. 对每一个字符判断处理的函数wordjudge调用次数也很多,但是要处理多少字符就要调用,多少次函数,所以也没有太多优化空间。
    3. 对词组和词的存入判断占用也较大,但是也是情理之中。
    4. 遍历词组和词哈希表找前十倒是没有花多少时间。
    5. 为了提高时间效率,开了较大的空间,属于时间和空间的互换。

    PSP表格

    测试结果

    VS下(左助教右我的)

     

    Linux下(左我的右助教)

    可以看到,在两种测试环境下,前十单词和词组都和助教都是一样的。但是两种环境下的单词字符和行数都有数百的误差,实在不是很清楚原因。

    项目经历及总结分析

    1. 一开始我的畏难情绪十分严重,因为自己实在比较菜,除了c和dev啥也没用过,所以这星期的任务实在太多。所以我选择先写完其它作业再全力肝软工。感觉这个决定很正确。因为我在构思的时候也确实收到了一些写了博客的同学的启发。
    2. 本次作业,周一周二写完基本函数模块,周三一轮测试,发现不行并进行了大的重构。周四中午完成VS上的调试,周四晚10点完成Linux下的移植。
    3. 一开始词和词组的哈希表是26*26*26*26的数组,按照前4个字母排序。由于这种方式和题目要求契合的较好,我一度觉得这是很好的解决方案。但是运行到大文件的时候还是出现了读取非常慢的情况。几经考虑后决定加大数组容量,并优化词组的存储方式和哈希函数,使程序明显清晰了不少,速度和稳定性都有提升。
    4. 这次个人作业让我了解了VS的一些基本操作,以及使用GitHub,遍历文件夹,以及Linux的一些简单命令和操作,还有关于程序移植方面的知识,以及练习了一些调试的技能,可以说是逼着狠狠恶补了一把。
    5. 这次软工还我明白了时间分配的重要性,从周一一直肝到周五,欠下了一堆的作业,上课的状态也不是很好,之后又要花好多时间来补,而且马上期中考又要加实验课,实在伤不起。以后要么更新技能,提高水平,要么“有舍才有得”/无奈。
  • 相关阅读:
    修改Ubuntu从文本界面登录
    Putty等工具中解决SSH连接超时断开的问题
    QoS policy-map class-map
    Linux中的do{...} while(0)
    手动增加swap分区
    __attribute__ 机制详解(一)
    欢迎来语雀关注我
    WebForm 生成并显示二维码
    《C#图解教程》 总览
    C#图解教程 第二十五章 其他主题
  • 原文地址:https://www.cnblogs.com/ustcccj/p/8678755.html
Copyright © 2011-2022 走看看