文件操作与字符处理_综合性报告(华南农业大学_高级程序语言设计)

华南农业大学的同学们,看到这里估计你们是不会/没时间写综合性实验了。

我这里给你们提供一些思路和个人见解,希望你们能顺利完成这道题目咯。加油加油!

【本文章仅供学习使用,未经允许禁止转载】

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)

不理解的可以在评论区提问!

  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值