Linux 状态机计算单词数量与统计单词出现频率


     状态机用于统计状态间切换次数,比较经典的应用在统计单词出现的次数等。在单词数量统计中,定义状态机为 IN OUT两个状态,分别表示当前位置在单词内与单词外,类似于光标在单词内与外的移动。初始化状态与光标移动到非字母处为out状态,检测到字母进入in状态,如图所示。

#define OUT       0
#define IN        1

#define INIT    OUT
int status = INIT;
int word = 0;

FILE *fp = fopen(filename, "r");
if (fp == NULL) return -1;
char c;
while ((c = fgetc(fp)) != EOF) {

if (' ' == c) || ('\n' == c) || ('\t' == c) ||
   ('\"' == c) || ('\'' == c) || ('+' == c) ||
   (',' == c) || ';' == c || '.' == c) { //
            status = OUT;
    } else if (OUT == status) {
            status = IN;
            word ++;
    }

} 

       打开一个文件,读取一个字符循环检测是否到达文章末尾,如未到达则检测当前字符是否为分隔符等非单词字符,否则当前字符为组成单词的字母。当检测到费单词部分时,进入out状态,检测到字母并且此时为out状态时才能进入IN状态,并记录进入IN状态次数,即为所得到的的单词个数。

        以此为基础,统计各个单词出现的频率,此时需要一个单词结构体,记录单词的字母组成,长度长度与出现次数。定义长度用于判断单词是否重复时,可以更快的排除不同的单词,避免每次都从头进行循环比较。

typedef  struct word {
    char word[30]; //假设没有多于三十个字符的单词
    int count;
    int length;
}word;
word w[20000];

        在判断单词部分与上文代码构造上基本相同,但输入的字符暂存在一个数组中,用于在输入时判断是否有相同单词,如果有则将元单词技术加一并不再存储重复的新单词,在存储单词数组中不创建重复单词的位置。

        当有第二个单词到来时,并且要记录单字母单词,到来时,判断已经存储的单词中是否有长度相同的单词,如果有再次判断是否与长度相同单词字母相同,如果是,相应的单词频率加一,break出当前与所存储单词的比较循环中。如未发现相应的单词,则新创建一个单词位置存储,在下次比较循环中,加入循环。当然可以选择用链表进行操作,每个单词存储时,在原链表后面加入一项,在遍历查找时,循环向后一个节点直到为空。

#include <stdio.h>
#include <string.h>

#define  OUT  0
#define  IN   1
#define  INIT OUT

typedef  struct word {
    char word[30];
    int count;
    int length;
}word;
word w[20000];


int  count_word(char* filename) {

    int status = INIT;
    char c[50];
    int word = -1;
    int letter = 0;
    int record = 0;
    FILE* fp = fopen(filename, "r");
    if (fp == NULL) return -1;
    while ((c[letter] = fgetc(fp)) != EOF) {

        if ((' ' == c[letter]) || ('\n' == c[letter]) || ('\t' == c[letter]) || ('\"' == c[letter]) || ('\'' == c[letter]) || ('+' == c[letter]) || (',' == c[letter]) || (';' == c[letter]) || ('-' == c[letter]) || ('\r' == c[letter])) 
        {
            if ((word > 1) && (letter>=1))
            {
                for (int i = 0; i < word; i++)
                {
                    if (w[i].length == letter)
                    {
                        int sum = 0;

                        for (int j = 0; j < letter; j++) {
                            if (w[i].word[j] != c[j])
                            {
                                sum++;           //判断长度相同的不同单词  当时因为因为什么 
                                                 //写成这样我也忘了
                            }
                        }

                        if (0 == sum) {
                            w[i].count++;
                            record = 1;        //表示次单词已经有记录在案了  不用重新建档

                            break;
                        }
                        sum = 0;

                    }

                }
            }
        if(record==0)  // 检查完毕  建档保存
        {
            for (int k = 0; k < letter; k++)
            w[word].word[k] = c[k];
            w[word].length = letter;
            w[word].count = 1;
            
        }
            
            status = OUT;
            letter = -1;
        }
        else if( OUT == status ){
            status = IN;
            if (record != 1)
            {
                word++;
            }
            if (record == 1)   //进入新单词中,取消之前的已经存在标志位
            {
                record = 0;
            }

        }   
        letter++;
        

    }

    return word;

}

int main(int argc, char* argv[]) {


    if (argc < 2) return -1;
    printf("word : %d \r\n",count_word(argv[1]));
    for (int i = 0; i < count_word(argv[1]); i++)
    {
        
            printf("letter %d : %s  :%d \r\n", i, w[i].word, w[i].count);
    }

}

        统计频率的代码没有区分大小写,见谅。后面也考虑过,或许可以使用单词字母的ASCII值来判断是否在内部,然后将其余一路置为out状态,可以试试。

        好像又出现了篇幅过短问题,此文章质量较低,这次的账号什么外链都没得,也没有图片全是代码,那估计是字太少了吧,凑点数。上一个账号就是因为GG违规被冻结GG了,这次学乖了,分享归分享,我也不挂连接也不同步社区了,如果有侵权的请联系我删除~~~确实想简单了,寻思参与个什么什么计划凑个奖,哪有那么简单呢,在人家的地盘发小广告,确实不对~~~ 消消停停发点帖子吧~~~~                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值