熟悉串类型的实现方法和文本模式的匹配方法,并初步熟悉文本处理方法和文字统计的方法。在实际应用中掌握数据结构所学的知识,并提高变成能力。
本程序实现了对文本的各种输入、删除和展示的需求,较大程度上满足了文学文本编辑的要求。文本采取分行保存,每行是动态双向链表,行内字符数组是静态的。字符匹配是对每行文字匹配目标串(目标串不分行),采用KMP算法进行匹配提高匹配效率。
。
。
#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");
}