数据结构课程实践作业 文学研究助手(原创于2021年) C语言

  • 实验目的

熟悉串类型的实现方法和文本模式的匹配方法,并初步熟悉文本处理方法和文字统计的方法。在实际应用中掌握数据结构所学的知识,并提高变成能力。

  • 程序概况

本程序实现了对文本的各种输入、删除和展示的需求,较大程度上满足了文学文本编辑的要求。文本采取分行保存,每行是动态双向链表,行内字符数组是静态的。字符匹配是对每行文字匹配目标串(目标串不分行),采用KMP算法进行匹配提高匹配效率。

数据结构课程实践作业 文学研究助手(原创于2021年) C语言_第1张图片

数据结构课程实践作业 文学研究助手(原创于2021年) C语言_第2张图片

数据结构课程实践作业 文学研究助手(原创于2021年) C语言_第3张图片

源代码:

#include
#include
#include

#define READNAME "edit.doc"
#define SAVENAME "edited.doc"
#define MAXLINENUM 256
#define MAXNUM 1025//一行最多字符
//                                  文本编辑功能

//数据结构
typedef struct line
{
	char content[MAXNUM];//保存本行的字符内容
	struct line* next;//下一行
	struct line* last;//上一行
}Line;

typedef struct Edit
{
	Line* head;//记录文本的开头行指针
	Line* tail;//记录文本的尾巴行指针
	int SumLine;//总的行数
	unsigned long long SumChar;//总的字符数,占一个字节
	char ReadName[50];
	char SaveName[50];
}Edit;


//                                  函数区
void InitEdit(Edit* edit);//初始化文本编辑器
void DestroyEdit(Edit* edit);//销毁文本编辑器,释放动态内存
void EditFromLast(Edit* edit);//从文本的最后边,换行继续编辑
void DisplayText(const Edit* edit);//显示所有的文本内容
//插入功能
int Insert(Edit* edit, int line, int col, char* s);//在某行某列的位置插入字符串
int InsertAtLast(Edit* edit, int line, char* s);//在行末插入插入字符串
int InsertLine(Edit* edit, int line, char* s);//在某行后面插入一行文字
void InsertLines(Edit* edit);//在某行后面插入一段文字
void Insert_menu();//插入函数菜单
void To_Insert(Edit* edit);//插入函数
//
//查找函数
void GetNext(char* tar, int* next);//获取tar字符串的next数组
//在p行查找单词tar,返回记录出现的次数
int FindWordAtLine(char* tar, Line* p, int* next, int* pos);//pos数组记录出现的下标
//统计单词在全文出现的总次数,具体出现在某行的情况
void StatisticWord(char* tar, const Edit* eidt);
void To_Statistic(const Edit* eidt);//统计函数

//删除函数
int DeleteStr(Edit* edit, int line, int col, int num);//删除line行col列开始的num个字符,返回是否删除成功
int DeleteLine(Edit* edit, int line);//删除第line行
int DeleteLines(Edit* edit, int line, int num);//删除从line行开始的num行
void To_Delete(Edit* edit);//删除
void Delete_Menu();//删除功能菜单

//文件操作函数
void ReadFile(Edit* edit, char* filename);//从文件filename中读取文本,缓存到edit中
void SaveText(const Edit* edit, char* filename);//将文本保存到文件filename中

//文本编辑功能管理函数
void EditFunc(Edit* edit);//实现跳转至文本编辑的各个函数函数
void Edit_Menu();//操作菜单

int main()
{
	system("mode con cols=120 lines=40");
	system("color 0B");//背景与字体颜色
	char filename[MAXNUM];
	Edit edit;
	while (1)
	{
		system("cls");
		printf("            ********************************* 欢迎使用文学研究助手 *********************************\n\n");
		printf("                                               [1]读取已有文本\n\n");
		printf("                                               [2]创建新文本\n\n");
		printf("                                               [3]退出系统\n\n");
		printf("            请输入相应序号:");
		int choice;
		scanf("%d", &choice);
		InitEdit(&edit);//初始化
		switch (choice)
		{
		case 1:
			printf("请输入要打开的文件:");
			scanf("%s", filename);
			ReadFile(&edit, filename);
			EditFunc(&edit);
			break;
		case 2:EditFunc(&edit); break;
		case 3:return 0; break;
		default:printf("输入有误,请重新输入!\n");
		}

	}
	return 0;
}


//                 函数实现区
//初始化文本编辑管理数据
void InitEdit(Edit* edit)
{
	edit->SumChar = 0;
	edit->SumLine = 0;
	//分配头指针地址,但不保存数据,并初始化头尾指针和next,last等指针
	edit->head = (Line*)malloc(sizeof(Line));
	edit->head->next = NULL;
	edit->head->last = NULL;
	edit->tail = NULL;

	//初始化文件路径
	strcpy(edit->ReadName, READNAME);
	strcpy(edit->SaveName, SAVENAME);
}
void DestroyEdit(Edit* edit)
{
	Line* p = edit->head->next;
	Line* tmp;
	while (p)
	{
		tmp = p->next;
		free(p);
		p = tmp;
	}
	//free(edit);
}
//输入函数
void EditFromLast(Edit* edit)
{
	char buf[MAXLINENUM];
	printf("请输入要输入的内容(每行以换行符结束,最后以CTRL+Z加回车结束输入):\n");
	char ch;
	int ct = 0;
	while ((ch = getchar()) != EOF)
	{
		if (ch != '\n')
		{
			buf[ct] = ch;
			ct++;
		}
		else//换行
		{
			buf[ct] = '\0';//添加结束符
			InsertLine(edit, edit->SumLine, buf);
			ct = 0;
		}
	}
}
void DisplayText(const Edit* edit)
{
	int line_ct = 1;//记录行号
	Line* p = edit->head->next;
	while (p)
	{
		printf("[%4d]%s\n", line_ct, p->content);
		line_ct++;
		p = p->next;
	}
}
int Insert(Edit* edit, int line, int col, char* s)
{
	//先判断数据是否合法
	if (line <= 0 || line > edit->SumLine)
	{
		printf("插入位置不合法!\n");
		return 0;
	}
	Line* p = edit->head->next;
	int ct = 1;//记录当前行号
	//定位到位置 
	if (line == edit->SumLine)p = edit->tail;//末尾行,直接定位,减少时间
	else
	{
		while (p && ct < line)
		{
			p = p->next;
			ct++;
		}
	}
	//插入
	int len = strlen(p->content);//原行的长度
	if (len + 1 < col || col <= 0)
	{
		printf("插入位置不合法!\n");
		return 0;
	}
	int len_s = strlen(s);
	int sumLen = len + len_s + 1;
	if (sumLen > MAXNUM)
	{
		printf("本行字符已经达到上限!插入失败。。。\n");
		return 0;
	}
	char buf[MAXNUM];
	col--;
	for (int i = 0; i < col; i++)
		buf[i] = p->content[i];//插入位置以前的 
	for (int i = col, j = 0; j < len_s; j++, i++)
		buf[i] = s[j];//插入位置的 
	for (int i = col + len_s, j = col; j < len; i++, j++)
		buf[i] = p->content[j];//插入位置以后的
	buf[sumLen - 1] = '\0';//结束符
	strcpy(p->content, buf);//赋值

	//更新字符总数
	edit->SumChar += strlen(s);
	return 1;
}
int InsertAtLast(Edit* edit, int line, char* s)
{
	//先判断数据是否合法
	if (line <= 0 || line > edit->SumLine)
	{
		printf("插入位置不合法!\n");
		return 0;
	}
	Line* p = edit->head->next;
	int ct = 1;//记录当前行号
	//定位到位置 
	if (line == edit->SumLine)p = edit->tail;//末尾行,直接定位,减少时间
	else
	{
		while (p && ct < line)
		{
			p = p->next;
			ct++;
		}
	}

	//插入
	int len_s = strlen(s);
	int sumLen = strlen(p->content) + len_s + 1;
	if (sumLen > MAXNUM)
	{
		printf("本行字符已经达到上限!插入失败。。。\n");
		return 0;
	}
	strcat(p->content, s);
	p->content[sumLen - 1] = '\0';

	//更新字符总数
	edit->SumChar += len_s;

	return 1;
}
int InsertLine(Edit* edit, int line, char* s)
{
	//先判断插入位置是否合法
	if (line > edit->SumLine || line < 0)return 0;//不合法
	Line* p = (Line*)malloc(sizeof(Line));
	p->next = NULL;
	strcpy(p->content, s);//赋值

	//插在文本开头 
	if (line == 0)
	{
		//正向链表
		Line* tmp = edit->head->next;
		edit->head->next = p;
		p->next = tmp;
		//反向链表
		if (tmp)//已有节点
		{
			p->last = edit->head;
			tmp->last = p;
		}
		else//无节点
		{
			p->last = edit->head;
		}
		//判断尾指针是否初始化
		if (edit->tail == NULL)edit->tail = p;
	}
	//插在文本末尾,tai为空的情况上一个语句已经解决
	else if (line == edit->SumLine)
	{
		Line* tmp = edit->tail;
		//正向
		edit->tail->next = p;
		edit->tail = p;
		//反向
		p->last = tmp;
	}
	else
	{
		Line* tmp = edit->head->next;
		int line_ct = 1;
		while (line_ct < line)
		{
			line_ct++;
			tmp = tmp->next;
		}
		//插入
		//正向
		Line* t = tmp->next;
		tmp->next = p;
		p->next = t;
		//反向
		p->last = tmp;
		t->last = p;
	}

	//更新字符总数,行数
	edit->SumChar += strlen(s);
	edit->SumLine++;
}
void InsertLines(Edit* edit)
{
	char ch;
	printf("****************插入一段文字****************\n");
	printf("输入行号,按回车,然后输入内容(每行以换行符结束,最后以按CTRL+Z加回车结束)\n");
	int line;
	char s[256];
	int ct = 0;
	scanf("%d", &line);
	getchar();//吸收换行
	while ((ch = getchar()) != EOF)
	{
		//先判断,是否是换行
		if (ch == '\n' || ch == EOF)
		{
			s[ct] = '\0';
			ct = 0;
			InsertLine(edit, line, s);
		}
		else s[ct++] = ch;
	}
}
void Insert_menu()
{
	printf("            ********************************* 文本插入 *********************************\n\n");
	printf("                                      0.返回上一级菜单\n\n");
	printf("                                      1.在某行某列插入字符串\n\n");
	printf("                                      2.在某行结尾插入字符串\n\n");
	printf("                                      3.在某行后面插入一行文字\n\n");
	printf("                                      4.在某行后面插入一段文字\n\n");
	printf("                                      5.在文本末尾继续编辑\n");
}
void To_Insert(Edit* edit)
{
	int choice = 0;
	int line, col;
	char s[256];
	while (1)
	{
		int ct = 0;
		char ch;

		system("cls");
		printf("原文:\n");
		DisplayText(edit);
		Insert_menu();
		scanf("%d", &choice);
		switch (choice)
		{
		case 0:return; break;
		case 1://某行某列处插入字符串 
			printf("请输入行号 列号,按回车,然后输入内容,按回车结束\n");
			scanf("%d %d", &line, &col);
			getchar();//吸收换行 
			if (line > 0 && line <= edit->SumLine && col > 0)
			{
				while ((ch = getchar()) != '\n')
					s[ct++] = ch;
				s[ct] = '\0';
				Insert(edit, line, col, s);
				printf("插入完成!\n");
			}
			break;
		case 2://某行末尾插入字符串 
			printf("请输入行号、按回车,然后输入内容,按回车结束本行输入(输入-1结束插入)\n");
			scanf("%d", &line);
			getchar();//吸收换行 
			if (line > 0)
			{
				while ((ch = getchar()) != '\n')
					s[ct++] = ch;
				s[ct] = '\0';
				InsertAtLast(edit, line, s);
				printf("插入完成!\n");
			}
			break;
		case 3://插入一行
			printf("请输入行号、按回车,然后输入内容,按回车结束本行输入(输入-1结束插入)\n");
			scanf("%d", &line);
			getchar();//吸收换行
			if (line >= 0)
			{
				while ((ch = getchar()) != '\n')
					s[ct++] = ch;
				s[ct] = '\0';
				InsertLine(edit, line, s);
				printf("插入完成!\n");
			}
			break;
		case 4://插入一段文字
			printf("请输入行号、按回车,然后输入内容,按CTRL+Z结束输入(输入-1结束插入)\n");
			scanf("%d", &line);
			getchar();//吸收换行
			if (line >= 0)
			{
				while ((ch = getchar()) != EOF)
				{
					if (ch == '\n')
					{
						s[ct] = '\0';
						ct = 0;//置为零
						InsertLine(edit, line, s);
						line++;//行号增加
					}
					else
					{
						s[ct++] = ch;
					}
				}
				//处理最后一行
				if (ct)
				{
					s[ct] = '\0';
					InsertLine(edit, line, s);
				}
				printf("插入完成!\n");
			}
			break;
		case 5:
			getchar();
			printf("请输入内容:\n");
			while ((ch = getchar()) != EOF)
			{

				if (ch == '\n')
				{
					s[ct] = '\0';
					ct = 0;//置为零
					InsertLine(edit, edit->SumLine, s);
					line++;//行号增加
				}
				else
				{
					s[ct++] = ch;
				}
			}
			//处理最后一行
			if (ct)
			{
				s[ct] = '\0';
				InsertLine(edit, edit->SumLine, s);
			}
			printf("插入完成!\n");
			break;
		default:printf("输入有误,请重新输入!\n"); break;
		}
		system("pause");
		system("cls");
	}
}

//查找函数
//在p行查找单词tar,返回记录出现的次数
//利用KMP算法,查找
void GetNext(char* tar, int* next)
{
	int len = strlen(tar);//获取目标串的长度
	int i = 0, j = -1;
	next[0] = -1;//
	while (i < len)
	{
		if (j == -1 || tar[i] == tar[j])
		{
			i++;
			j++;
			next[i] = j;
		}
		else
		{
			j = next[j];
		}
	}

	//优化next数组
	for (i = 1; i < len; i++)
		if (i != 0 && tar[i] == tar[next[i]])next[i] = next[next[i]];//i!=0
}
int FindWordAtLine(char* tar, Line* p, int* next, int* pos)
{
	int len_tar = strlen(tar);//目标串的长度
	int len_p = strlen(p->content);//母串的长度
	int i = -1, j = -1;
	int ct = 0;//记录匹配成功的次数
	while (j < len_p)
	{
		while (i < len_tar && j < len_p)
		{
			if (i == -1 || tar[i] == p->content[j])
			{
				i++;
				j++;
			}
			else i = next[i];
		}
		//判断是否匹配成功
		if (i >= len_tar)
		{
			pos[ct] = j - len_tar;//记录匹配成功时,开头字符的下标
			ct++;//匹配成功,继续匹配
			i = -1;//
			j--;
		}
		else break;//匹配失败,直接退出
	}
	return ct;//返回匹配成功的次数
}
void StatisticWord(char* tar, const Edit* edit)
{
	Line* p = edit->head->next;
	int sum = 0, line_ct = 0;//sum记录出现的总次数,line_ct记录每行出现的次数
	int ct = 1;//记录行号
	int tar_next[MAXNUM];
	int pos[MAXNUM];//记录单词tar开头字符出现的下标
	GetNext(tar, tar_next);//获取next数组
	while (p)
	{
		line_ct = FindWordAtLine(tar, p, tar_next, pos);
		if (line_ct)printf("\n【%d】行出现%d次,列号分别为:%d", ct, line_ct, pos[0] + 1);
		for (int i = 1; i < line_ct; i++)printf("、%d", pos[i] + 1);
		ct++;//行号
		sum += line_ct;
		p = p->next;//迭代 
	}
	printf("\n%s在本文中总共出现%d次\n", tar, sum);
}
void To_Statistic(const Edit* edit)
{
	printf("原文:\n");
	DisplayText(edit);
	char tar[MAXNUM];
	printf("请输入要查询的单词或句子(保证在所有字符同一行出现):");
	char ch;
	int i = 0;
	getchar();//吸收换行符 
	while (i < MAXNUM && (ch = getchar()) != '\n')
		tar[i++] = ch;
	if (i == MAXNUM)
	{
		printf("要查询的单词或句子太长了。。。\n");
		return;
	}
	tar[i] = '\0';//tar结束标志
	StatisticWord(tar, edit);
}

int DeleteStr(Edit* edit, int line, int col, int num)
{
	//判断位置是否合法
	if (line<1 || line>edit->SumLine)return 0;


	int len = 0;//记录一行的字符数
	Line* p = NULL;
	//末尾行
	if (line == edit->SumLine)
	{
		p = edit->tail;
	}
	else
	{
		int line_ct = 1;
		p = edit->head->next;
		while (p)
		{
			if (line_ct == line)break;
			else
			{
				line_ct++;
				p = p->next;
			}
		}
	}
	len = strlen(p->content);
	if (len < num)return 0;
	int i, j;
	for (i = 0, j = col + num - 1; p->content[j]; i++, j++)
		p->content[i + col - 1] = p->content[j];
	p->content[i + col - 1] = '\0';//结束符
	//更新字符数
	edit->SumChar -= len;
}
int DeleteLine(Edit* edit, int line)
{
	//先判断是否合法
	if (line<1 || line > edit->SumLine)return 0;

	int word_ct = 0;//记录删除的字符数
	//若是末尾行
	if (line == edit->SumLine)
	{
		Line* tmp = edit->tail;
		tmp->last->next = NULL;
		word_ct = strlen(tmp->content);//计算字符数
		free(tmp);//释放内存
	}
	else
	{
		int line_ct = 1;
		Line* p = edit->head->next;
		while (p)
		{
			if (line_ct == line)
			{
				//正向
				p->last->next = p->next;
				//反向
				p->next->last = p->last;
				word_ct = strlen(p->content);
				free(p);//释放内存
				break;
			}
			else
			{
				line_ct++;
				p = p->next;
			}
		}
	}

	//更新行数和字符数
	edit->SumChar -= word_ct;
	edit->SumLine--;
}
void To_Delete(Edit* edit)
{
	system("cls");
	int line = 0, col = 0, num = 0;
	int choice = 0;
	while (1)
	{
		printf("原文:\n");
		DisplayText(edit);
		Delete_Menu();
		printf("请输入选项:");
		scanf("%d", &choice);
		switch (choice)
		{
		case 0:return; break;
		case 1:
			printf("请输入 行号 列号 个数:");
			scanf("%d %d %d", &line, &col, &num);
			DeleteStr(edit, line, col, num);
			break;
		case 2:
			printf("请输入 行号:");
			scanf("%d", &line);
			DeleteLine(edit, line);
			break;
		case 3:
			printf("请输入 行号 行数:");
			scanf("%d %d", &line, &num);
			DeleteLines(edit, line, num);
			break;
		default:printf("输入有误,请重新输入!\n"); break;
		}
		system("pause");
		system("cls");
	}
}
void Delete_Menu()
{
	printf("            ********************************* 文本删除 *********************************\n\n");
	printf("                                         0.返回上一级菜单\n\n");
	printf("                                         1.从某行某列删除N个字符\n\n");
	printf("                                         2.删除某行字符\n\n");
	printf("                                         3.从某行开始删除N行\n\n");
}

//文本操作函数
void ReadFile(Edit* edit, char* filename)
{
	FILE* fp;
	fp = fopen(filename, "r");
	if (!fp)
	{
		printf("文件%s读取失败!\n", filename);
		return;
	}
	char ch;
	char buf[MAXNUM];
	int ct = 0;
	InitEdit(edit);//初始化文本编辑变量
	//读取文本
	while (!feof(fp))
	{
		ch = fgetc(fp);
		//判断是否是换行符
		if (ch == '\n')
		{
			buf[ct] = '\0';//结束符
			InsertLine(edit, edit->SumLine, buf);
			ct = 0;//置为零
		}
		else buf[ct++] = ch;
	}
	//文本结束后
	if (ct)
	{
		buf[ct] = '\0';//结束符
		InsertLine(edit, edit->SumLine, buf);
		ct = 0;//置为零
	}
	fclose(fp);
	printf("文件读取成功!\n");
}
void SaveText(const Edit* edit, char* filename)
{
	FILE* fp;
	fp = fopen(filename, "w");
	if (!fp)
	{
		printf("文件%s创建失败!\n", filename);
		return;
	}
	Line* p = edit->head->next;
	if (p)
	{
		fprintf(fp, "%s", p->content);
		p = p->next;//迭代
	}
	while (p)
	{
		fprintf(fp, "\n%s", p->content);
		p = p->next;//迭代
	}
	fclose(fp);
	printf("文本已保存到%s中。\n", filename);
}
int DeleteLines(Edit* edit, int line, int num)
{
	//检测数据是否合法
	if (line<0 || line>edit->SumLine || num<0 || line + num - 1>edit->SumLine)return 0;

	for (int i = 0; i < num; i++)
		DeleteLine(edit, line);
	return 1;
}
void EditFunc(Edit* edit)//实现跳转至文本编辑的各个函数函数
{
	system("cls");
	int choice;
	char filename[MAXNUM];
	while (1)
	{
		system("cls");
		Edit_Menu();
		printf("请输入选项:");
		scanf("%d", &choice);
		switch (choice)
		{
		case 0:DestroyEdit(edit); return;//返回,并销毁
		case 1:
			To_Insert(edit);
			break;
		case 2:
			To_Delete(edit);
			break;
		case 3:
			To_Statistic(edit);
			break;
		case 4:
			printf("请输入文件名:");
			scanf("%s", filename);
			SaveText(edit, filename);
			break;
		case 5:DisplayText(edit);
			printf("总共%d行,%d个字符。\n", edit->SumLine, edit->SumChar);
			break;

		default:printf("输入有误,请重新输入!\n");
			break;
		}
		system("pause");
		system("cls");
	}
}
void Edit_Menu()//操作菜单
{
	printf("            ********************************* 文本研究助手 *********************************\n\n");
	printf("                                              0.返回上一级页面\n\n");
	printf("                                              1.输入文字\n\n");
	printf("                                              2.删除文字\n\n");
	printf("                                              3.查找文字\n\n");
	printf("                                              4.保存文本\n\n");
	printf("                                              5.阅读文本\n\n");
}

觉得写得不错,点个赞吧,让我知道你的赞赏!

你可能感兴趣的:(数据结构与算法学习笔记,数据结构,c语言)