SCAU 高程綜合實驗:檔案操作與字元處理

吃花椒的妙醬發表於2020-12-26

SCAU 高程綜合實驗:檔案操作與字元處理

在當前目錄中存在檔名為"case1.in"(其中case後為數字1,不是字母l,寫錯提交後會判錯)的文字檔案,
其內容為一篇英文文章(以EOF作為結束標誌)。現要求讀取該文字檔案內容,統計文章中每個單詞出現的次數,
並輸出出現次數最多的前5個單詞及其出現次數(按出現次數由多到少的順序輸出,次數相同時按字典順序輸出,
不足5個單詞時,按序輸出全部單詞)。程式中注意如下細節:
(1) 空格、標點符號與回車符起到分隔單詞的作用。
(2) 文章一行的末尾可能有連字元,出現連字元時,該行最末的字串與下行最先出現的字串構一個單詞;
(3) 名詞縮寫算一個單詞;
(4) 數字不算單詞;
(5) 單詞不區分大小寫;
(6) 輸出時單詞全使用小寫;

#include “stdio.h”
#include “math.h”
#include “string.h”
#include “stdlib.h”


main()
{
_______________________
}

輸入格式
檔案case1.in中一篇英文文章,包含多段文字,單詞數不超過10000,每個單詞不超過20個字元

輸出格式
按題意輸出答案

輸入樣例
(如case1.in內容如下)
I am a student. My school is SCAU. It is a beau-
tiful university. I like it.

輸出樣例
a 2
i 2
is 2
it 2
am 1

解題思路分析:
按照解題順序,分析一下幾個重要的點:
第一個考點是檔案操作,檔案的開啟關閉和讀寫。題目中說掃描單詞,又由於有字串的特殊要求,所以使用fscanf有點小困難,還是使用fgetc逐字掃描、判斷。
第二個考點是單詞的判斷,也是這道題的核心,這個和前面oj上一題對單詞的判斷讀寫幾乎相同,只是多了個連字元的問題。當掃描到有單詞結束的符號(空格、標點等),打上標記,有連字元+換行的話,另作標記。
第三個考點是字串的存放以及字串的排序,當然其中也不可避免的有string庫中函式的運用。由於掃描完單詞後便存入陣列,自然要用到字串陣列,其中也變相的考察了學生地址(指標)的概念。排序的話自然是字典序,由於能力有限,使用冒泡+strcpy進行排序。

下面貼一下我的程式碼(含詳註)。

#include "stdio.h"
#include "math.h"
#include "string.h"
#include "stdlib.h"
char sto[10005][25];//字串陣列,無重複 
int num=0;//字串陣列下標 
int count[10005]; //每個單詞出現的次數 
int isalpha(char ch) //判斷是否為字母(函式庫裡竟然沒有isalpha)
{
	if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')) return 1;
	else return 0;
}
int repeat(char *s)//判斷單詞是否重複
{
	int i;
	for(i=0;i<num;i++)
	{
		if(!strcmp(sto[i],s))//查重 
		{if(i==0) return 20000;else return i;}//查到重複的單詞,則返回單詞第一次出現的下標 
	}
	return 0;//沒查到,則返回0 
}

main()
{         
	FILE *fp;
	int i,j=0,k;
	char ch;
	int endflag=0,lianflag=0,enter=0;
	for(i=0;i<10005;i++) count[i]=1;//計數器初始化為1
	
	fp=fopen("case1.in","r+");//開啟檔案 
	if(fp==NULL) {printf("ERROR");return 0;}
	
	while((ch=fgetc(fp))!=EOF)//由於有連字元,用fscanf有點困難,所以開始逐字掃描操作,
	{
		if(isalpha(ch)&&ch>='A'&&ch<='Z') ch+=32; 
		if(isalpha(ch)&&endflag&&!lianflag&&!enter) {enter=0;endflag=0;lianflag=0;j=0;num++;sto[num][j++]=ch;}//新單詞的情況,更新連字元、回車、單詞結束的標記,建立新一行陣列記錄下一個單詞 
		else if(isalpha(ch)&&endflag==0&&!lianflag&&!enter) {enter=0;lianflag=0;sto[num][j++]=ch;}//同一個單詞的情況,即單詞續寫 
		else if(ch==' '||(lianflag==0&&ch=='\n')||((ch!='-')&&((ch>=32&&ch<=47)||(ch>=49&&ch<=64)||(ch>=91&&ch<=96)||(ch>=123&&ch<=126))))//單詞結束,檢索到符號、換行,就換單詞 
		{
			enter=0;endflag=1;lianflag=0;
			int p=0;
			if(p=repeat(sto[num])) {if(p==20000) p=0;memset(sto[num],0,sizeof(sto[num]));num--;count[p]++;}
		}
		else if(isalpha(ch)&&lianflag&&!enter)//有連字元但無回車的特殊情況,算新單詞的出現,也標誌著連字元前一個單詞的結束,對前一個單詞進行查重計數,然後進行新單詞建立的操作 
		 {int p=0;
		if(p=repeat(sto[num])) {if(p==20000) p=0;memset(sto[num],0,sizeof(sto[num]));num--;count[p]++;}
		enter=0;endflag=0;lianflag=0;j=0;num++;sto[num][j++]=ch;
		} 

		else if(ch=='-'&&endflag==0)	{enter=0;lianflag=1;}//檢索到連字元標記一下 
		else if(ch=='\n') {enter=1;}//檢索到換行標記一下 
		else if(lianflag&&isalpha(ch)&&!endflag&&enter) {enter=0;lianflag=0;sto[num][j++]=ch;}//有連字元+回車的情況,是原來的單詞,進行單詞的續寫 
		else {endflag=1;lianflag=0;if(repeat(sto[num])) {memset(sto[num],0,sizeof(sto[num]));num--;count[repeat(sto[num])]++;}} //其餘情況(包括數字)算作無單詞,進行標記的清除 	
	}
	fclose(fp);//檔案掃描結束,關閉檔案 
	char t[25];//下面將用到冒泡,建立第三方變數 
	int t2=0;//同理,計數器也要用到冒泡 
	
	for(i=0;i<num-1;i++)//將字串陣列按照字典序排序,使用氣泡排序 
	for(j=0;j<num-1-i;j++)
	if(strcmp(sto[j],sto[j+1])>0) 
	{memset(t,0,sizeof(t));strcpy(t,sto[j]);t2=count[j];strcpy(sto[j],sto[j+1]);count[j]=count[j+1];strcpy(sto[j+1],t);count[j+1]=t2;} 

	int p=0;//掃描次數前五的單詞,如果不足五個就直接return 0 
	int max1=0,max2=0,max3=0,max4=0,max5=0; //由於只有五個,就不對次數進行排序了,直接比較依次求最多次數就好,比完把最多的次數清零 
	
	for(i=0;i<=num;i++) if(max1<count[i]) {p=i;max1=count[i];}
	count[p]=0;
	printf("%s %d\n",sto[p],max1);
	
	for(i=0;i<=num;i++) if(max2<count[i]) {p=i;max2=count[i];}
	if(max2==0) return 0; 
	else
	{
	count[p]=0;
	printf("%s %d\n",sto[p],max2);
	}
	
	for(i=0;i<=num;i++) if(max3<count[i]) {p=i;max3=count[i];}
	if(max3==0) return 0;
	else
	{
	count[p]=0;
	printf("%s %d\n",sto[p],max3);
	}
	
	for(i=0;i<=num;i++) if(max4<count[i]) {p=i;max4=count[i];}
	if(max4==0) return 0;
	else
	{
	count[p]=0;
	printf("%s %d\n",sto[p],max4);
	}
	
	for(i=0;i<=num;i++) if(max5<count[i]) {p=i;max5=count[i];}
	if(max5==0) return 0;
	else 
	{ 
	count[p]=0;
	printf("%s %d\n",sto[p],max5);
	}
	return 0;
}

總結與心得:
綜合性實驗主要考查本學期c語言所學語法的綜合運用,靈活多變,思維量不大,實驗過程中細節和注意點較多。
儘管實驗大體思路清晰,但是在我的實踐過程並不是一帆風順。一開始沒有注意題目中連字元的規定,用了fscanf,看到測試資料t-t-t-t人傻了(這貨算4個單詞???),然後從零開始。很多知識點的細節沒徹底瞭解,也增添了我程式中的小bug。
通過本次實驗,我明白了平時學習知識,除了重點練習知識點主體部分,許多細節和注意點也要多多留意。題目先看清看完整,大體思路想明白寫清楚,再動手敲程式碼。有錯就改,改錯也是學知識不可或缺的一個環節,也是完善知識的一個過程,許多問題也是在上機實踐過程中才發現自己不會寫,然後通過搜尋引擎或者和小夥伴、老師的討論交流,去補上自己知識層面的漏洞或者改善自己程式碼。

相關文章