文本文件单词统计(C语言)
本人目前还是一位在校大学生,写文章的目的是为了记录一下当前所学,与其他爱好者或从业者相互学习交流。文本文件单词统计这个题目是学校的一次课程设计中的一道,在编写时也是参考了很多资料,借鉴了一些别人的想法,如有冒犯我会及时删除。同样,我的编写成果肯定会存在一些不足之处,也请读者多多包涵。
这个题目的具体信息如下:
1、问题描述
编写一个文本文件单词统计的程序,包括建立文件、单词统计、单词查询、单词定位的功能。
2、基本要求
程序应先询问用户的 ID号(ID 号包括两个大写字母和4 位数字),例如:请输入用户 ID 号:AB1234 ,程序应对输入的 ID 号验证,符合 ID 号要求的格式(检验ID并不是主要要求,大家可自行忽略),然后程序提示四种选择:
(1) 建立文件
(2) 单词统计
(3) 单词查询及定位
(4) 退出
注意:
i) 文件至少包含50个英文单词(一定出现重复的单词,且一定包含数字)
ii) 文档不规范,单词之间可能不只有一个空格,并且有加引号的一些单词“jiaozi” 加引号的单词算单词,但数字不算单词
iii) 逐行扫描文本文件,计算文档中单词总数,及各个单词出现的频次,并且按照单词首字母abcd…… 的顺序排列,显示并生成soft.txt文件
iv) 查询任意给出的单词是否存在,如果不存在,在屏幕上输出“查无此词!”;如果存在,显示单词出现的总次数,并且详细列出其出现的位置。
例如:
请您输入待查询的词:of
单词 of 共出现了2次;
第1次出现在第1行,第5个位置;
第2次出现在第3行,第1个位置。
请您输入待查询的词:
3、问题分析
解决如下问题:
1.检测用户ID格式是否正确,要求(1)用户ID位数为6;(2)前两位为大写英文字母,后四位是数字;
2.可创建文本文件并可输入内容;
3.对文本文件中的单词数进行统计,要求:(1)输出时将单词按照字典顺序排序;(2)数字不算入总数;(3)引号内的单词也算入总数;
4.实现单词定位,要求(1)输出单词出现的详细位置;(2)输出该单词出现的次数
1.4、功能实现
i.检验用户ID:用户从键盘中输入一个字符串。程序将接受的字符串赋值到一个字符数组,遍历数组中的元素判断格式是否正确。
ii.建立文本文件:(1)定义一个串变量;(2)定义文本文件;(3)输入文件名,打开该文件;(4)循环读入文本行,写入文本文件,其过程如下:
While(不是文件输入结束){
读入一文本行至串变量;
串变量写入文件;
输入是否结束输入标志;}
(5)关闭文件。
iii.朴素匹配算法:该算法是整个单词检索程序的核心。对于主串的每一个字符作为子串的开头,与要匹配的字符串进行匹配,对主串做最大的循环,每个字符开头都做子串长度的小循环,直到匹配成功或者全部遍历完成为止。更具体地说:设有三个指针i,j,k,用i指示主串S每次开始比较的位置;指针j,k分别指示主串S和模式串T中当前正在等待比较的字符位置;一开始从主串S的第一个字符(i=0;j=1)和模式T的第一个字符(k=0)比较,若相等,则继续逐个比较后续字符(j++,k++)。否则从主串的下一个字符(i++)起再重新和模式串(j=0)的字符开始比较。依此类推,直到模式T中的所有字符都比较完,而且一直相等,则称匹配成功,并返回位置 i;否则返回-1,表示匹配失败。(可能讲述地不清楚,读者可再查询其他资料进行学习)
iv. 单词统计:
单词总个数统计:在下文提到的单词出现频率统计的过程结束之后,将每个单词出现的次数相加就是单词的总个数。
单词之间有多个空格。在进行判断时,若空格之后仍然是空格,则多个空格作为一个空格进行处理。
单词出现频率统计:逐行扫描文本,将不同的单词放在一个字符数组中。在扫描过程中,每遇到一个与已保存在数组中的某一个单词相同的单词的时候,该单词的计数加1。
按字典顺序排序:将数组中的每个元素(即单词)采用冒泡法排序,最后输出详细的单词出现频率。
v.单词查询:遍历保存单词的字符数组,将要查询的单词与数组元素挨个比对。若不存在,则显示“查无此词”。
vi.单词定位:确定要检索的文本文件,确定要检索的单词,逐行扫描文本,将所要查询的单词与文本比对,若相同则立即输出行数和相应位置。
源代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
typedef struct{
char str[50]; //单词最大长度设为50
int cnt; //单词出现次数
}Str; //存放一个单词
int vcount(char* p){ //统计文本中单词个数
FILE *fp;
char temp[50]; //在进行冒泡排序时临时存放单词
char tmp[50]; //用于在从文件中读取单词时临时存放单词
Str words[200]; //单词数量上限(200)
int num=0,count=0;//实际单词数量:num的值为不同的单词个数,count的值为总单词个数
int i,j,neww=1; //neww标志位,判断是否为新单词
int m,n;
if((fp=fopen(p,"r"))==NULL){
printf("找不到文件\n");
return -1;
}
loop: while(fscanf(fp,"%s",tmp)!=EOF){ //fscanf从文件中挨个读取单个字符串 (单词)
neww=1;
for(i=0;i<50;i++){ //若读入的是数字则读取下一个字符串(因为数字不计入单词数)
if(tmp[i]>='0'&&tmp[i]<='9'){
goto loop;
}
}
for(i=0;i<50;i++){ //读入的字符串(单词)中可能包含标点符号",""."和自定义的结束标记"$" ,遇到这些符号将字符串断开
if(tmp[i]=='$'||tmp[i]==','||tmp[i]=='.')
tmp[i]='\0';
}
for(i=0; i<num; i++){
//重复的单词
if (strcmp(tmp, words[i].str)==0 ){ //和已经读取的单词进行比对,看是否为新单词。若不是,则单词出现次数加1
neww=0;
words[i].cnt++;
}
}
if (neww){ //若是新单词,则复制字符串进入word数组
for (j=0; tmp[j]!='\0'; j++) {
words[num].str[j] = tmp[j];
}
words[num].str[j] = '\0'; //单词末尾添加结束符
words[num++].cnt = 1; // 新单词数量+1
}
}
for(m=0;m<num-1;m++){ //采用冒泡排序实现单词的字典排序
for(n=0;n<num-1;n++){
if(strcmp(words[n].str,words[n+1].str)<0) //A>B,返回正数;A<B,返回负数;A=B,返回0
{
strcpy(temp,words[n].str); //数字可以用“=”交换,字符串用strcpy函数
strcpy(words[n].str,words[n+1].str);
strcpy(words[n+1].str,temp);
}
}
}
for(m=0;m<num;m++){ //统计单词总数
count += words[m].cnt;
}
printf("一共%d个不同的单词,每个单词出现次数如下:\n",num);//输出信息
for (i=num-1; i>=0; i--) {
printf("%-10s %2d\n", words[i].str, words[i].cnt);
}
printf("单词个数为(包含重复出现)%d\n",count);
fclose(fp);
}
int Index(char* p){ //朴素模式匹配算法
int j=0,i=0;//j用于记录输入的子串的长度,i用于记录主串的长度
char S[1000],T[50]; //s[]存放文件中的字符,t[]存放要搜索的单词
char ch1,ch2;
FILE *fp;
if((fp=fopen(p,"r"))==NULL){
printf("找不到文件\n");
return -1;
}
ch1=fgetc(fp); //将文件读取到一维数组当中
while(ch1!='$'){
S[i]=ch1;
i++;
ch1=fgetc(fp);
}
S[i]='$';
printf("请输入你要搜索的单词:\n"); //建立子串(输入想要搜索的单词)
scanf("%s",T);
ch2=T[0];
while(ch2!='\0'){ //j代表了所要搜索的单词的长度
j++;
ch2=T[j];
}
//朴素模式匹配算法实现
int row=1,col=1;//row为行,col为列
int m=0,n=0,q=1;//m为S(主串)的下标,n为T(子串)的下标,q用于换行时记录m的值
int num=0;//num用于记录选定单词出现的次数
int left=0;//判断左右引号
while(m<i){
if(S[m]=='"'){ //加引号的单词处理:跳过引号,读取下一个字符,并设置标记left=1
m=m+1;
left=1;
}
if(S[m]==T[n]){
m++;
n++;
}
else{
m=m-n+1;
n=0;
};
if(n==j&&S[m+1]!=' '){ //匹配到相应单词
num++; //出现次数加1
if(row==1){
col=m-n+1;
printf("第%d次出现的位置位于第%d行第%d列\n",num,row,col);
}
else{
col=m-n-q;
printf("第%d次出现的位置位于第%d行第%d列\n",num,row,col);
}
}
if(S[m]==' '||(S[m]=='"' &&left==1)){ //遇到右引号的情况下和空格进行一样的处理
col++;
left=0;
while(S[m]==' '||S[m]=='"')
m=m+1;
}
if(S[m]=='\n'){
row++;
q=m;
};
if(S[m]=='$'){
printf("所选单词出现的次数是%d\n",num);
return num;
}
}
}
void createfile(char* p){ //创建文件
FILE *fp;
char ch;
fp=fopen(p,"w+");
ch=getchar(); //写入文件
printf("请输入单词:(请以$符号结束)\n");
while(ch!='$')
{
ch=getchar();
fputc(ch,fp);
}
fclose(fp);
}
int checkID(char s[],int x) { // 检验ID ID格式;共6位 前两位大写字母 后四位为数字
int i = 0,y = x;
char t[6];
if(strlen(s)!=6){
printf("您输入的格式不正确(位数不是6位),请重新输入!\n");
return 1;
}
for(i=0;i<y;i++){
t[i] = s[i];
}
for(i=0;i<y;i++){ //判断前两位
if(i<2){
if(t[i]>='A'&&t[i]<='Z') ;
else {
printf("您输入的格式不正确(前两位不是大写英文字母),请重新输入!\n");
return 1;
break;
}
}
if(i>=2){ //判断后四位
if(t[i]>='0'&&t[i]<='9') ;
else {
printf("您输入的格式不正确(后四位中包含非数字字符),请重新输入!\n",i+1);
return 1;
break;
}
}
}
}
int main(){
int j = 1,l = 0;
int count=-1,c=-1;
char filename[255],afilename[255];
char *p;
char ID[6];
char quit;
while(j==1){ // 调用cheakID函数对用户名进行判断
printf("请输入您的ID!(前两位为大写字母,后四位为整数)\n");
scanf("%s",ID);
j = checkID(ID,sizeof(ID));
if(j==0){
printf("输入正确!");
}
}
while(true){
printf("请选择您想要进行的操作(请输入操作前对应的数字)\n 1.建立文件\n 2.单词查询及定位\n 3.单词统计\n 4.退出\n");
scanf("%d",&l);
fflush(stdin); //吃掉回车符
if(l!=1&&l!=2&&l!=3&&l!=4){
printf("您的输入有误,请重新输入!");
}
if(l==1){
printf("请输入所要创建的文件名(包括扩展名)");
scanf("%[^\n]",filename);
p=filename;
createfile(p);
printf("文件创建成功!"); // 建立文件
}
if(l==2){ // 具体单词定位及查询
while(count==-1){
printf("请输入文件名(包括扩展名)");
scanf("%[^\n]",afilename);
fflush(stdin);
p=afilename;
count=Index(p);
if(count==0){
printf("查无此词!");
}
if(count==-1){
printf("文件名有误,请重新进入查询!\n");
break;
}
}
}
if(l==3){
while(c==-1){
printf("请输入文件名(包括扩展名)");
scanf("%[^\n]",afilename);
fflush(stdin);
p=afilename;
c=vcount(p);
if(c==-1){
printf("文件名有误,请重新进入查询!\n");
break;
}
}
}
if(l==4){
printf("您已退出该程序!");
break; //退出练习程序
}
}
}
部分运行结果截图: