}|Si-1,Si∈D,Si-1基本操作:
InitList(OrderList*L)
操作结果:
构造一个空的有序表;
DestroyList(OrderList*L)
初始条件:
有序表L已存在;
操作结果:
销毁L的结构,并释放所占空间;
LocateElem(OrderListL,ElemTypee,LinkType*q)
初始条件:
有序表L已存在;
操作结果:
若有序表L中存在元素e,则q指示L中第一个值为e的元素的位置,
并返回函数值FRUE;否则q指示第一个大于e的元素的前驱的位置,
并返回函数值FALSE;
InsertAfter(OrderList*L,LinkTypeq,LinkTypes)
初始条件:
有序表L已存在,q指示L中一个元素;
操作结果:
在有序表L中q指示的元素之后插入元素s;
ListCompare(OrderListLa,OrderListLb,EqelemList*s)
初始条件:
有序表La和Lb已存在;
操作结果:
以s返回其中相同元素;
}ADTOrderList
3.定义单词文本串文件类型如下:
ADTTextString{
数据对象:
D={Si|Si∈标准c字符集,i=1,2,3,…….,n,n≥0};
数据关系:
D中字符被“换行符”分割成若干行,每一行的字符间满足下列关系:
R1={}|Si-1,Si∈D,i=1,2,3,…..,n}
基本操作:
Initialization(FILE**fr)
初始条件:
文件fr已存在;
操作结果:
打开文件fr,设定文件指针指向文件中第一行第一个字符;
GetAWord(FILE*f,Sequence*st)
初始条件:
文件f已打开;
操作结果:
从文件指针所指字符起提取一个“单词st”;
ExtractWord(FILE*f,OrderList*ta)
初始条件:
文件f已打开,文件指针指向文件f中某一行的第一个字符;
操作结果:
提取该行中所有单词,并构成单词的有序表ta,本操作结束时,文件指针
指向文件f中下一行的第一个字符;
match(FILE*f,OrderListpat,ResultTypers)
初始条件:
文件f已打开,文件指针指向文件f中第一个字符;pat为包含所有待查
询单词的有序表;
操作结果:
rs为查询结果;
}ADTTextString
4.本程序包含四个模块:
1)主程序模块:
主函数设计如下
intmain(){
输入信息和文件初始化;
生成测试目标词汇表;
统计文件中每个待测单词出现的次数和位置;
输出测试结果;
};
2)单词单元模块-------实现单词类型;
3)有序表单元模块--------实现有序表类型;
4)单词文本串文件单元模块------实现文本串文件类型;
5、存储结构及存储映像为:
6、函数的调用关系为:
main
InitializationInputWordListEmptyInitRListOutResultmatchDestroyList
InitListMakeNodeNewWordWordCmpInsertAfterPrintWord
MakeNodeCopyWordExtractWordListCompareNewLength
FelonGetAWordMakeNodeInsertAfter
三、详细设计:
//LiteratureAssitant.cpp:
Definestheentrypointfortheconsoleapplication.
//
#include"stdafx.h"
#include"stdafx.h"
//LiteratureAssitant.cpp:
定义控制台应用程序的入口点。
#include"stdafx.h"
#include"stdio.h"
#include"string.h"
#include
#include
#include
#defineMAXSIZE10000//字符空间的最大容量
#defineMAXLEN20//假设一个单词的最大长度不超过20个字符
#defineMAXNUM16//假设文本文件中一行的单词数不超过16个
#defineFALSE0
#defineTRUE1
typedefintstatus;
/*
单词采用堆式存储结构,即分配一块较大的存储空间作为堆的存储空间,
待分析的特定单词集放入堆空间中,文本文件中分解出的一行单词也放入堆空间中。
由于一行单词分析完后就没有用途了,所以可以释放一行单词所占用的存储空间。
*/
typedefstruct//堆结构的定义
{
charstores[MAXSIZE];
intfreep;//当前可用空间开始位置
}HeapSpace;
HeapSpacesp;//由于堆操作在该程序中是最频繁的操作,可以把表示堆空间的变量定义为全程变量,
//单词所占的堆空间,初始化令sp.freep=0
/*
单词通常用其在堆存储空间中的存储映像来表示。
一个单词的值尽管在堆空间中,如果丢失了其存储映像,也就相当于丢失了单词。
单词在存储空间中的存储映像可以定义为:
*/
typedefstruct//单词类型定义
{
intstadr;//单词在堆空间中的位置
intlen;//单词长度
}WordType;
typedefstruct//一个单词可以用其一个字符数组和字符串的长度来表示
{
charch[MAXLEN];
intsize;
}Sequence;
/*在实验中,待分析的单词构成一个单词集。
文本文件中的一行作为一个基本分析单位,一行中的单词也构成一个单词集。
统计特定单词在文本文件中出现的次数和位置(行号)的基本方法是:
用文本文件中分解出的一行的每个单词去查找待分析的特定单词集,
若该单词出现在特定单词集中,则该单词的统计次数加1,并记录下出现的行号。
为了便于单词查找,可以把单词集组织成一个按从小到大升序排列的有序表。
由于单词的个数不固定,用链式存储结构来实现有序表。
有序表的每个结点表示一个单词的存储映像。
有序表类型定义:
*/
typedefWordTypeElemType;//元素类型
typedefstructNodeType//单词有序表结点定义
{
ElemTypedata;
structNodeType*next;
}NodeType,*LinkType;//结点类型,指针类型
typedefstruct
{//单词有序表定义
LinkTypehead;/*有序表头指针*/
LinkTypetail;/*有序表尾指针*/
intsize;/*有序表结点个数*/
}OrderList;
typedefstruct//单词在目标词汇表中的位置
{
inteqelem[MAXNUM];//数组eqelem中存放的是单词在特定单词集在有序表中结点序号,
//该序号与分析结果链表的表头结点向量的序号一致,
//表示要在对应链表中插入一个表示当前分析行的节点。
intlast;//整数last表示在处理过程中要把匹配的单词结点的序号放入eqelem数组的位置,
//最终表示有多少个单词获得匹配
}EqelemList;
/*按照要求,程序的核心功能是要统计特定单词在文本文件中出现的次数和位置。
因此次存放分析结果的数据结构中既要指明统计的是哪个单词,又要记录出现的次数和位置。
要统计的单词已放防在堆空间中,其存储映像已由单词有序表表示。
一个单词出现的位置的记录与出现的次数有关,
但事先是无法估计一个单词在一个文本文件中约出现多少次,
因此用链表结构来记录单词出现的位置是合适的选择,
即一个单词在文件中出现的位置用一个单链表来记录。
假定,要统计的特定单词从键盘输入,不超过一行,即单词个数不超过MAXNUM。
为了表示分析结果的上述信息,又方便管理,
可以将所有分析单词信息和链表的头指针信息共同构成一个向量结构。
文件测试相关的数据类型定义*/
typedefstructNode
{//单词在文件中的位置
intelem;//被测单词在文件中的行号
structNode*next;//指向下一个行号结点的指针
}Node,*Link;
typedefstruct
{//单词统计分析记录结构定义
WordTypedata;//被测试的单词
intcount;//在文件中出现的次数
LinkNext;//记录出现的所有行号的链表头指针
}HeadNode;
/*文本文件测试结果记录定义*/
typedefHeadNodeResultType[MAXNUM];
/*
统计分析的总体思路
(1)从键盘输入待统计分析的特定单词集,将单词放入堆存出空间中,并建立单词集的存储映像有序表。
(2)从键盘输入待分析的文本文件名并打开文件。
(3)顺序分析文本文件的每一行,直到文件结束。
对每一行:
①识别出该行的每个单词,把单词放入堆空间中,并建立单词存储映像有序表。
②用该行分解出的单词与特定单词集中单词进匹配,记录下匹配的结果。
③建立结点记录相应单词出现的行号,插入到相应链表中,并累加统计次数。
(4)输出统计结果数据。
输出要统计的单词、单词在文件中出现的次数、单词出现的位置(行号)的集合。
*/
statusNewWord(WordType*nw,Sequencecha)
{/*算法的功能是把单词放入堆空间中,并建立单词在堆中的存储映像*/
inti,k;
if(sp.freep+cha.size>=MAXSIZE)
{
printf("HeapFull!
\n");
getchar();
return0;
}
else
{
i=sp.freep;
sp.freep=sp.freep+cha.size;
for(k=0;ksp.stores[i+k]=cha.ch[k];
nw->stadr=i;
nw->len=cha.size;
return1;
}//判断空间是否已满
}
/*------------------------------回到最初空间-------------------*/
voidNewLength(OrderListrs)
{
intm=0;
LinkTypep,q;
p=rs.head->next;
while(p)
{
if(m<=p->data.stadr)
{
m=p->data.stadr;
q=p;
}p=p->next;
}
sp.freep=m+q->data.len;
}
voidCopyWord(WordType*nw,WordTypeoldw)
{
nw->stadr=oldw.stadr;
nw->len=oldw.len;//将单词信息拷贝到另一个结点上
}
intWordCmp(WordTypewd1,WordTypewd2)//判断是否为新单词
{
intk,si,sj,m;
si=wd1.stadr;
sj=wd2.stadr;
for(k=0;k<=wd1.len&&k<=wd2.len;k++)
{
m=fabs((float)(sp.stores[si+k]-sp.stores[sj+k]));
if(m!
=0&&m!
=32)
break;
if(k==wd1.len||k==wd2.len)
break;
}
if(wd1.len==wd2.len)
{
if(k==wd1.len)
return0;
elseif(sp.stores[si+k]>sp.stores[sj+k])
return1;
else
return-1;
}
elseif(wd1.len{
if(k==wd1.len)
return-1;
elseif(sp.stores[si+k]>sp.stores[sj+k])
return1;
else
return-1;
}else
{
if(k==wd2.len)
return1;
elseif(sp.stores[si+k]>sp.stores[sj+k])
return1;
else
return-1;
}
}
voidPrintWord(WordTypewd)
{
inti;
for(i=0;iputchar(sp.stores[wd.stadr+i]);
}
statusMakeNode(LinkType*p,ElemTypee)
{
*p=(LinkType)malloc(sizeof(NodeType));
if(!
(*p))
returnFALSE;
(*p)->data.stadr=e.stadr;
(*p)->data.len=e.len;
(*p)->next=NULL;
returnTRUE;//建立存储需查询的单词的链表的结点
}
statusInitList(OrderList*L)
{
ElemTypewd;
wd.len=0;
if(MakeNode(&(L->head),wd))
{
L->tail=L->head;
L->head->next=NULL;
L->size=0;
returnTRUE;
}else
{
L->head=NULL;
returnFALSE;
}
}//建立链表
voidDestroyList(OrderList*L)
{
LinkTypep,q;
p=L->head;
while(p)
{
q=p;
p=p->next;
free(q);
}
L->head=L->tail=NULL;
}//销毁有序表
statusLocateElem(OrderListL,ElemTypee,LinkType*q)
{
LinkTypep;
p=L.head->next;
while(p)
{
if(WordCmp(p->data,e)==0)
{*q=p;
returnTRUE;
}
if(WordCmp(p->data,e)==-1)
*q=p;
p=p->next;
}
returnFALSE;
}
voidInsertAfter(OrderList*L,LinkTypeq,LinkTypes)
{
if(L->head&&q&&s)
{
s->next=q->next;
q->next=s;
if(L->tail==q)
L->tail=s;
L->size++;
}
}
voidListCompare(OrderListLa,OrderListLb,EqelemList*s)
{
intpos,n;
LinkTypepa,pb;
if(La.head&&Lb.head)
{
pa=La.head->next;
pb=Lb.head->next;
s->last=pos=0;
while(pa&&pb)
{
n=WordCmp(pa->data,pb->data);
if(n==0)
{
s->eqelem[s->last++]=pos++;
pa=pa->next;
pb=pb->next;
}
elseif(n==-1)
{
pa=pa->next;
pos++;
}
elsepb=pb->next;
}
}
}
statusListEmpty(OrderList*L)
{
if(L->size==0)
returnTRUE;
returnFALSE;
}
intListLength(OrderList*L)
{/*返回判表长度*/
if(L->head==L->tail)
return0;
else
returnL->size;
}
intfeoln(FILE*f)
{//判断文件中下一个字符是否为回车符
//返回值:
0--不是回车符;1--是回车符
charcha;
cha=fgetc(f);
if(cha=='\n')
return(TRUE);
ungetc(cha,f);//将你读到的字符回退到输入流中
returnFALSE;
}
voidGetAWord(FILE*f,Sequence*st)
{/*假定文件中单词或是字母字符序列、或是整数/小数型数字字符序列、或是界符,
由此作为判断单词结束的依据。
算法基本思想:
首先读掉单词前面的所有空格。
若单词第一个字符是字母,则连续读取后面所有字母字符组成一个单词;
若单词第一个字符是数字,则连续读取后面包括小数点在内的所有字母字符组成一个单词;
若是界符,则有单个界符字符组成一个单词。
*/
charch;
intk=0;
ch=fgetc(f);
while(ch=='')
{ch=fgetc(f);
if(ch=='\n')
break;}/*去掉空格和回车*/
if(ch=='\n')
{
ungetc(ch,f);ch='';
}/*最后一个为回车键,文件指针回指*/
while(((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')||(ch>='0'&&ch<='9'))&&!
feof(f))
{
st->ch[k]=ch;
ch=fgetc(f);
k++;
}
if(ch=='\n')
ungetc(ch,f);
st->size=k;
}
statusExtractWord(FILE*f,OrderList*ta)
{/*文件中以‘\n’作为行结束符,每个单词之间空格符或界符隔开。
每个单词或是字母字符序列、或是整数/小数型数字字符序列、或是界符,
由此作为判断单词结束的依据。
在堆存储结构中,一行单词组成一个有序表。
若一行中有多个相同单词,则只算一个。
算法基本思想:
顺序读入一个单词,把该单词存入堆空间中,
通过查询确定该单词是否是一个新单词,若是新单词,
则建立存放该单词存储映像的结点,把结点插入到有序表的指定位置。
遇到行结束符是则算法结束。
*/
inti;
Sequencestr;
WordTypenwd;
LinkTypep,q;
LinkTypes;
if(!
InitList(ta))
returnFALSE;
p=ta->head;
q=ta->head;
for(i=0;!
feof(f);i++)
{
if(feoln(f))
returnTRUE;
GetAWord(f,&str);//从文件中读出一个单词
NewWord(&nwd,str);//将单词放入堆存储空间
MakeNode(&s,nwd);//生成一个单词节点
if(ta->head->next)
{