题目还有介绍就不说了,直接说下方案改进
在上一版中,采用了两个结构体,一个是存单词,一个是做字典,这一版中,通过改善读取单词,换取了简单的结构体形式来处理。
一个存单词并记录单词出现次数的结构体,一个存这个结构体存储了多少个单词并且通过此结构体上一结构体调用动态内存,说的好像不清楚,直接上代码
#define WORD_LEN 80
#define number 10
typedef struct word
{
char Nword[WORD_LEN];
int count;
}word;
typedef struct wordtable
{
word *w;
int Wnumber;
int len;
}wordtable;
这里第二个结构体是第一个结构体的表,当前动态内存的个数,有效单词的个数,指向第一个单词的结构体的指针
这里宏定义了两个变量,一个WORD_LEN,默认单词长度,一个number,第一次调用的动态内存个数,也算是尽量减少内存浪费,所以实现所需内存跟着单词数量走,最初十个,不够用了就扩大二倍。
上一版程序运行速度并不快,当时大概要八秒左右,后边分析,主要原因是从文件读入单词时候,一个一个读取,磁盘io太频繁,所以导致速度慢,所以这次直接测文件字节大小,将所有数据一次读入到数组中,然后进行单词划分存入,所以最后运行两秒差不多就有结果了。
这一版代码的划分单词思想还是基本一样的,或许代码略微精简了一些,划分单词存单词计数是很连贯的一系列操作完成的,
大概步骤如下:
1.划分得到一个单词
2.存该单词(预处理)
3.查找该单词是否存在于单词结构体中,存在返回下标,否则返回-1
4.获得若是下标值,则计数器加一,否则将新单词存入最后一个单词后边
5.存入时检测当前动态内存是否用完,若是用完,内存扩大二倍,否者插入单词
所以代码要简洁一些。并且因为这次动态内存的创建实在单词表的结构体中,所以最后要单写一个销毁动态内存的函数操作,下边附上第二版完整代码,以及粗糙的注释 :)
//第二版
#define WORD_LEN 80//单词长度
#define number 10//动态内存个数
typedef struct word//存单词结构体
{
char Nword[WORD_LEN];
int count;
}word;
typedef struct wordtable//单词表结构体
{
word *w;
int Wnumber;
int len;
}wordtable;
void initwt(wordtable *wt)//单词表结构体初始化
{
wt->w = (word *)malloc(sizeof(word)*number);//申请最初定义的个数的动态内存
wt->Wnumber = 0;//初始化单词表存入单词个数为0
wt->len = number;//初始化初始最多可存单词数
}
int searchword(char *nw,wordtable *wt)//单词表中查找单词,成功返回下标,失败返回-1
{
for(int i=0;i < wt->Wnumber;++i)
{
if(strcmp(wt->w[i].Nword,nw)==0)
{
return i;
}
}
return -1;
}
void insertword(char *nw,wordtable *wt)//插入新单词
{
if(wt->Wnumber == wt->len)//判断当前动态内存是否满,若满就扩大内存为原来的二倍
{
wt->len *= 2;//更新最多可存单词数
wt->w = (word *)realloc(wt->w,sizeof(word)*wt->len);//扩存
}
strcpy(wt->w[wt->Wnumber].Nword,nw);//新单词插入
wt->w[wt->Wnumber].count = 1;//新单词计数器置1
wt->Wnumber ++;//单词表存入单词个数加一
}
void saveword(char *nw,wordtable *wt)//存单词操作
{
int i = searchword(nw,wt);//先查找单词
if(i != -1)
{
wt->w[i].count ++;//查找成功,计数器加一
}
else
{
insertword(nw,wt);//查找失败,插入该单词
}
}
void countsort(wordtable *wt)//单词出现次数排序
{
word tmp;
for(int i=0;i < wt->Wnumber;++i)
{
for(int j=i+1;j < wt->Wnumber;++j)
{
if(wt->w[i].count < wt->w[j].count)
{
tmp = wt->w[i];
wt->w[i] = wt->w[j];
wt->w[j] = tmp;
}
}
}
}
void writeN(const int n,const wordtable *wt)//将排名前N的结果输出值文件
{
FILE *fw = fopen("E:\\new.txt","w");
assert(fw !=NULL);
for(int i=0;i < n;++i)
{
fprintf(fw,"%s出现了%d次\n",wt->w[i].Nword,wt->w[i].count);
}
fclose(fw);
}
void destory(wordtable *wt)//动态内存销毁
{
free(wt->w);
}
void readfile(const char *path)//文件操作
{
FILE *fr = fopen(path,"r");//打开文件
assert(fr !=NULL);//判断打开是否成功
fseek(fr,0,SEEK_END);//光标移动至结尾
int file_size = ftell(fr);//计算文件大小
fseek(fr,0,SEEK_SET);//光标恢复至文件头
char *tmp_file = (char *)malloc(file_size*sizeof(char));//动态数组存储文件内容
fread(tmp_file,sizeof(char),file_size,fr);//将内容读入到数组
char newword[WORD_LEN];//用来等到划分好的单词
int j = 0;//划分单词所用的下标
bool flag = false;//标记是否成功得到单词
wordtable wt;//定义结构体变量
initwt(&wt);//初始化
for(int i=0;i < file_size;++i)//对数组中单词进行划分
{
if(isalpha((unsigned char)(tmp_file[i])))//因为文件中可能存在乱码使函数判断断言失败所以转符号为无符号字符判断
{
newword[j++] = tmp_file[i];//若是单词,赋值到单词块
flag = true;//标记开始得到一个单词
}
else//非字母字符操作
{
if(flag)//若是得到单词
{
newword[j] = '\0';//单词结尾标记'\0',单词编程字符串,方便操作
saveword(newword,&wt);//存单词
flag = false;//标记重置
j = 0;//下标重置
}
}
}
countsort(&wt);//排序
writeN(1000,&wt);//输出结果
fclose(fr);//关闭文件
destory(&wt);//释放内存
free(tmp_file);//释放动态数组内存
}
int main()
{
readfile("E:xiaoshuo1.txt");
return 0;
}
ok,这个小项目就结束啦,其实手法还是拙略罢了,所以看到的大佬不喜勿喷0.0