华南农业大学的同学们,看到这里估计你们是不会/没时间写综合性实验了。
我这里给你们提供一些思路和个人见解,希望你们能顺利完成这道题目咯。加油加油!
【本文章仅供学习使用,未经允许禁止转载】
1.题目&要求
文件操作与字符处理_综合性报告
在当前目录中存在文件名为"case1.in"(其中case后为数字1,不是字母l,写错提交后会判错)的文本文件,其内容为一篇英文文章(以EOF作为结束标志)。现要求读取该文本文件内容,统计文章中每个单词出现的次数,并输出出现次数最多的前5个单词及其出现次数(按出现次数由多到少的顺序输出,次数相同时按字典顺序输出,不足5个单词时,按序输出全部单词)。程序中注意如下细节:
(1) 空格、标点符号与回车符起到分隔单词的作用。
(2) 文章一行的末尾可能有连字符,出现连字符时,该行最末的字符串与下行最先出现的字符串构一个单词;
(3) 名词缩写算一个单词;
(4) 数字不算单词;
(5) 单词不区分大小写;
(6) 输出时单词全使用小写;
2.源代码
话不多说,先贴源代码
#include "stdio.h"
#include "math.h"
#include "string.h"
#include "stdlib.h"
#include <ctype.h>
int t=0;
int mem;
struct wo{
char word[21];
int num;
}re[10000];
int jud(char test[])
{
int i=0;
for(i=0;i<t;i++)
if(strcmp(test,re[i].word)==0)
{
re[i].num++;
mem=i;
return 0;
}
return 1;
}
int ret(char chuan[],int i)
{
int judd=0;
char *a=&chuan[i];
while(!isalpha(*a))
{
if(*a=='\0') return i+(a-&chuan[i])-1;
a++;
}
char *b=a;
while(isalpha(*b))
b++;
char tem[21];
int q=0,w=0;
for(q=0;a!=b;a++,q++)
tem[q]=*a;
tem[q]='\0';
char tem2[21];
memset(tem2,0,sizeof(tem2));
if(*a=='-' && *(a+1)=='\n' && isalpha(*(a+2)))
{
while(!isalpha(*a))
a++;
b=a;
while(isalpha(*b))
b++;
for(q=0;a!=b;a++,q++)
tem2[q]=*a;
tem2[q]='\0';
}
char *ttem=strcat(tem,tem2);
if(jud(ttem)==1)
{
for(w=0;w<sizeof(tem);w++)
re[t].word[w]=ttem[w];
re[t].word[w]='\0';
re[t].num=1;
mem=t;
t++;
}
if(*b=='\0') return i+(b-&chuan[i])-2;
return i+(b-&chuan[i])-1;
}
main()
{
FILE *fp;
fp=fopen("case1.in","r");
char ch,chuan[100000];
int n_max;
for(n_max=0;(ch=fgetc(fp))!=EOF;n_max++)
chuan[n_max]=tolower(ch);
chuan[n_max]=0;
int i=0,j=0,time=0;
char *st=chuan,*en=chuan;
for(i=0;i<n_max;i++)
i=ret(chuan,i);
for(i=0;i<5;i++)
{
int k=i;
for(j=i+1;j<t;j++)
if((re[k].num<re[j].num)||(re[k].num==re[j].num&&strcmp(re[k].word,re[j].word)>0))
k=j;
struct wo a =re[k];
re[k]=re[i];
re[i]=a;
}
for(i=0;i<5;i++)
printf("%s %d\n",re[i].word,re[i].num);
fclose(fp);
}
3.带注释代码
#include "stdio.h"
#include "math.h"
#include "string.h"
#include "stdlib.h"
#include <ctype.h>
//这个头文件呢适用于文本处理
//里面的函数比较方便
//感兴趣的可以了解一下
int t=0; //全局变量,用于记录单词的种类数量
int mem; //【没用,忘删了】
struct wo{ //我把结构数组的声明放在这里
char word[21]; //目的是是这个结构数组成为全局变量
int num; //让任何一个函数都可以直接访问和修改里面的元素
}re[10000];
//jud函数,返回整型变量,需要传递一个(待测试的)字符串
//用于判断某个单词是否已经出现
int jud(char test[])
{
int i=0;
for(i=0;i<t;i++) //遍历结构数组
{
if(strcmp(test,re[i].word)==0) //假如已出现过该字符串
{
re[i].num++; //该字符串对应的出现数量+1
mem=i; //【没用,忘删了】
return 0; //返回0代表不需要新增结构体
}
}
return 1; //返回1代表该单词未出现过
//需要新增结构体
}
//ret函数,返回整型变量
//用于整理字符串中的单词,放入结构数组中
int ret(char chuan[],int i)
{
int judd=0;
//第一部分
char *a=&chuan[i]; //我习惯用指针,当然用下标数字也可以,也许更好写一点
//isaipha(int)
//这个函数用于判断括号里面的字符是否为英文字母(不论大小写)
//如果是字母,就返回1/真;如果不是,就返回0/假
//但在这里,我前面加了一个感叹号“!”,代表否
while(!isalpha(*a)) //说明如果函数返回真,则为假...(学过计导计概的都会吧)
{
if(*a=='\0')
{ //这里防止检测到字符串结尾
return i+(a-&chuan[i])-1; //如果是就直接返回字符串结尾...的上一个字符的下标
} //注意!为什么可以直接返回呢 ?
//因为条件决定了如果是字母就不可能进入循环体
//一旦进入循环体了,那就说明前面没有检测到字母
a++; //如果不是就指针指向下一位字符
} //注意!循环结束后a位于单词的开始
char *b=a; //这里是已经检测到一个“单词”存在了
//a指针是一个单词的开始
//那我复制一下a指针
while(isalpha(*b)) //这个循环用于找到单词的末尾
{ //注意!为什么这里不需要上一个循环的判断语句?
b++; //因为这个字符串的结尾是空字符,一定不是字母
} //所以不用担心这回事
//注意!循环结束后b位于单词的末尾的后一个字符
char tem[21]; //声明了一个字符串用来放这个单词
int q=0,w=0;
for(q=0;a!=b;a++,q++) //放单词的过程
tem[q]=*a; //在这个循环结束后,a到了b的位置
//也就是单词的后一个字符
tem[q]='\0'; //不要忘了以空字符结尾
//第二部分
char tem2[21]; //创建一下第二个放单词的字符串
memset(tem2,0,sizeof(tem2)); //这个函数在stdlib.h里面,用于刷新数组
//这一步相当于初始化,tem2数组里面全部是空字符
//为什么要这样初始化?往下看(其实也是个人习惯)
if(*a=='-' && *(a+1)=='\n' && isalpha(*(a+2))) //这里就是看是不是后面有连字符、换行符和字母连着
//如果是那么前后合成一个单词
{
while(!isalpha(*a)) //接下来的过程和上面几乎一样
a++; //不过就是第一个循环
//因为已经检测到后面有单词,不可能字符串戛然而止
//所以不需要再加判断返回了
b=a;
while(isalpha(*b))
b++;
for(q=0;a!=b;a++,q++)
tem2[q]=*a;
tem2[q]='\0'; //经典字符串空字符结尾,别忘了
//当然其实这步没有也无所谓,因为我已经memset了
}
//第三部分
char *ttem=strcat(tem,tem2); //这里我直接把两个数组合成在一起
//其实不返回新数组也可以,直接用tem就行了
//也是个人习惯吧
//注意!假如后面不需要合成单词的话
//那么这里就合成了个寂寞。但是是合法的!
//【但是!如果我没有初始化,那么这一步就不合理了!】
//【因为strcat函数是】
//【把后面的字符串替换前面字符串的第一个空字符】
//【那么这么做前面的字符串就没有空字符了,很危险!】
if(jud(ttem)==1) //需要增加结构数组的元素
{
for(w=0;w<sizeof(tem);w++) //这一步看似是我失误了,把ttem打成tem
re[t].word[w]=ttem[w]; //实际上确实是我失误了,但是没有影响
//因为strcat吧tem2传到tem的末尾,再把tem返回给ttem
//实际上ttem和tem是相等的,所以没有完全失误
re[t].word[w]='\0'; //不要忘了结尾空字符
re[t].num=1; //初始化为1
mem=t; //【没用,忘删了】
t++;
}
if(*b=='\0') return i+(b-&chuan[i])-2; //如果b是空字符的话,就返回空字符前一个位置的下标
return i+(b-&chuan[i])-1; //如果不是就返回b的下标
//现在看来其实没有必要
//因为反正空字符也会超出条件范围退出循环的
//直接返回 i+(b-&chuan[i])-1 就行了
//这也是我为什么说直接用下标不用指针可能会好写一点
}
main()
{
FILE *fp;
if((fp=fopen("case1.in","r"))==NULL)//其实这里直接写
{ //fp=fopen("case1.in","r");
printf("NO"); //就可以了
return 0; //这么做纯粹个人习惯
}
char ch,chuan[100000]; //这里创建了一个足够大的字符串用于接收文本
int n_max; //代表了这个文本的长度( 有多少字符)
for(n_max = 0;(ch = fgetc(fp)) != EOF;n_max ++)
{
chuan[n_max] = tolower(ch); //tolower(int) 这个来自于ctype.h的函数
//如果接受到大写字母,就返回对应的小写字母
//如果不是,就返回原来的字符
} //等价于
//if(chuan[n_max]>='A' && chuan[n_max]<='Z') chuan[n_max]-=32;
chuan[n_max]=0; //字符串以空字符结尾,也可以写'\0'
int i=0,j=0,time=0; //【 time 没用但是忘删了】
char *st=chuan,*en=chuan; //【没用但是忘删了】
for(i=0;i<n_max;i++) //字符处理函数
i=ret(chuan,i); //把字符串中的单词整理到结构数组中
for(i=0;i<5;i++) //一个简单的选择排序
{ //因为只输出5个所以 只排序5次
int k=i;
for(j=i+1;j<t;j++)
if((re[k].num<re[j].num)||(re[k].num==re[j].num&&strcmp(re[k].word,re[j].word)>0))
k=j;
struct wo a =re[k]; //直接交换结构体
re[k]=re[i];
re[i]=a;
}
for(i=0;i<5;i++)
printf("%s %d\n",re[i].word,re[i].num);
fclose(fp); //防止以免中间会出什么以外,关闭文件放在最后(前面也行的)
//没写return 0;
}
4.总结
总结一下以上内容,捋一下思路还是很不错的
之前我上课爆肝200行代码没肝出来,捋顺思路之后一晚上肝了不到100行就出来了,说明思路还是很重要的。
其实存放问字符串可以放在全局变量,这样就不用传来传去,而且元素的数量可以标大一点,要是现在的我就直接1e8上了(haha)。
还有就是我的数据结构设计还是很乱的,因为边写边想,就这样吧。
希望各位同学顺利完成代码和实验报告,好好学好专业课啊!
对了,不要照抄哦(xixi)
不理解的可以在评论区提问!
标签:字符,华南农业大学,re,int,++,单词,程序语言,char,chuan From: https://blog.csdn.net/2301_81246674/article/details/140804656