状态机用于统计状态间切换次数,比较经典的应用在统计单词出现的次数等。在单词数量统计中,定义状态机为 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了,这次学乖了,分享归分享,我也不挂连接也不同步社区了,如果有侵权的请联系我删除~~~确实想简单了,寻思参与个什么什么计划凑个奖,哪有那么简单呢,在人家的地盘发小广告,确实不对~~~ 消消停停发点帖子吧~~~~