简单行编辑程序
实验题目:简单行编辑程序
一,题目:
30、简单行编辑程序
[问题描述]
文本编辑程序是利用计算机进行文字加工的基本软件工具,实现对文本文件的插入、删除等修改操作。限制这些操作以行为单位进行的编辑程序称为行编辑程序。
被编辑的文本文件可能很大,全部读入编辑程序的数据空间(内存)的做法既不经济,
也不总能实现。一种解决方法是逐段地编辑。任何时刻只把待编辑文件的一段放在内存,称为活区。试按照这种方法实现一个简单的行编辑程序。设文件每行不超过 320 个字符,很少超过 80 字符。
[基本要求]
实现以下 4 条基本编辑命令:
(1) 行插入。格式:i<行号><回车><文本><回车>
将<文本>插入活区中第<行号>行之后
(2)行删除。格式:d<行号 1>[□<行号 2>]<回车>
删除活区中第<行号 1>行(到第<行号 2>行)。两种格式的例子是:“d10↙”和“d10□14↙”
(3)活区切换。格式:n<回车>
将活区写入输出文件,并从输入文件中读入下一段,作为新的活区。
(4)活区显示。格式:p<回车>
逐页地(每页 20 行)显示活区内容,每显示一页之后请用户决定是否继续显示以后各
页(如果存在)。印出的每一行要前置以行号和一个空格符,行号固定占 4 位,增量为 1。
各条命令中的行号均须在活区中各行行号范围之内,只有插入命令的行号可以等于活区
第一行行号减 1,表示插入当前屏幕中第一行之前,否则命令参数非法。
13 [选作内容] (1) 对于命令格式非法等一切错误作严格检查和适当处理。 (2) 加入更复杂的编辑操作,如对某行进行串替换;在活区内进行模式匹配
二、要解决的问题
(1) 行插入。
(2)行删除。
(3)活区切换。
(4)活区显示。
附加:
(5)在活区内进行多或单模式匹配
三、算法基本思想描述:
对于题目的要求,在进行活区切换及显示,行插入,行删除,模式匹配都要求涉及链表,所以对链表进行的基本操作在课设中有所体现,而在附加功能中,使用了AC自动机,进行匹配,来提高时间效率。
AC自动机,其实类似于字典树+KMP算法,通过构建匹配失败后的fail指针,来构建AC自动机的模式匹配树,而fail的构建涉及bfs算法,而fail的构建算法,首先先将连接于root的点的所有子节点连向root的点,再将所有非NULL的点压入队列,弹出进行操作,考虑节点失配的情况,让失配的节点p->next[i]->fail=p->fail->next[i];这个过程后就可以建立一颗带fail节点的字典树。
而在自动机的匹配过程中就是按照生成的fail树对应匹配。即匹配失败就移动到p->next[i]->fail.
顺带补充下字典树的建立,
本课设的字典树是采用指针版,即一个节点下有对应的30的子指针,代表a-z,然后如果为空即代表没有这个字母。如果不为空,这代表有这个字母,假设abc,fde建立的字典树如图:
四、设计
1. 数据结构的设计
(1)储存结构:
1.
const int MAXN = 81;
const int MAXNS = 1024;
char file_name[MAXNS]; ///储存用户输入的地址
char file_ends_name[MAXNS]; ///储存文本输出的地址
char AC_TIRE_ARR[325 * 25];///代表AC自动机要匹配的数组
2.读出和读入文件的数据结构:
typedef struct NODE
{
char words[MAXN];///代表每个节点中储存的数据
struct NODE *next;///指向下一个节点
int num; ///代表行数(供输出的时候使用)
bool flag; ///flag代表行结束,行没结束时,flag==false,结束时///将其标记为true
} node;
3.AC自动机中的字典树的数据结构
typedef struct TIRE
{
struct TIRE *next[30];///代表指向字典树子节点
bool flag;///代表匹配串是否结束了
struct TIRE *fail;///指向失配后的位置
} tire;
2.算法的设计
2.1函数的思路详解
(1)向系统申请node类型的空间,并返回给node型指针
node *creat()
(2)判断读取的数据是否为文件尾的数据
bool PD1(char word[]) ///判断行是否结束
其主要思路通过对数组word的strlen(word)-1来进行判断,因为txt文档中的数据读取一行的话,行末会带’\n’,所以’\n’可以代表一行的结束
(3)从文件中读取到链表中来构建数据。
int get_hang(char fileopenname[MAXNS], node *head, int move, int &fflag)
使用fgets函数从文件中读取一行中的81个字符到储存数据的链表中,然后在读取到一行结束的时候把node结构体中的flag=true,来标记结束。
(4)输出从文件中读取到链表的内容
void PRINTNODE(node *head) ///输出链表
通过head来读取数据,如果遇到flag==true的就证明,该行到达了结束,输出换行,接着输出该节点的序号,然后循环。
(5),清空函数
void clearl(node *p)///释放链表,防止内存泄露
void clear()///清屏函数
(6)///菜单生成表
void view()
(7)针对于添加后超出活区限制,提交到文件中
void only_insert(char strs[305], node *&head)///把第一行输出到文本中
因为题意中表明了,会出现添加超过了活区的情况,那么我就将活区链表的第一个节点提交到文件中,并且将第一个节点经行移动。然后重新建立序号。
(8)把修改后的活区读入文件中
void INPUT_file(char file_sname[MAXNS], node *head)
通过申请一个中转数组来储存第一行的数据,通过对链表的读取来储存数据,然后通过fput读入文件。
(9),代表删除一个行区间
其原理为
void del_file_one(int ans1, int ans2, node *&head) ///代表删除一个区间
采用两个指针来实现,一个指向要删除的行的前一个的节点,一个指向后一个节点,采用将前一个指针指向后一个指针来实现区间删除。
关于删除第一个节点,直接将头指针移动到下一个行节点。就可以了。
多出来的节点可以通过删除函数进行删除。
(10),删除单行
void del_file_two(int ans1, node *&head)
原理也是采用两个两个节点来跑,一个指向要删除的行的前一个的节点,一个指向后一个节点,采用将前一个指针指向后一个指针来实现单行删除。
原理图跟上述一样
(11),创建AC自动机的字典树部分的节点
tire *creat_tire()
申请一个节点,并且将其相连的子节点==NULL
然后把标记变成0;标记的意思是是否走到这个子匹配串的末尾。
(12),在字典树中插入匹配子串
一个节点下有对应的30的子指针,代表a-z,然后如果为空即代表没有这个字母。如果不为空,这代表有这个字母,假设abc,fde建立的字典树如图:
void insert_tire(char words[], tire *root)
思路就是对子串数组进行遍历,如果第一个节点下的对应节点为NULL的话就建立这个节点,而建立的方式有个核心,就是next【ans】,ans=words【i】-’a’;
来获取,应用了ascii码的运算
(13)生成fail指向
/**
通过构建匹配失败后的fail指针,来构建AC自动机的模式匹配树,而fail的构建涉及bfs算法,而fail的构建算法,首先先将连接于root的点的所有子节点连向root的点,再将所有非NULL的点压入队列,弹出进行操作,考虑节点失配的情况,让失配的节点p->next[i]->fail=p->fail->next[i];这个过程后就可以建立一颗带fail节点的字典树。涉及bfs,即广度优先搜索!
而在自动机的匹配过程中就是按照生成的fail树对应匹配。即匹配失败就移动到p->next[i]->fail.
**/
void build_ACtire(tire *root)
(14)查找匹配的位置
如图:
bool ACTIRE_search(char str[], tire *root)
将查找串与匹配串所建立的fail树进行匹配,如果匹配失败就访问匹配失败的fail指针再次经行匹配。匹配成功就返回匹配的的字段的位置。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cstdlib> #include <queue> #include <vector> using namespace std; const int MAXN = 81; const int MAXNS = 1024; char file_name[MAXNS]; ///用户输入的地址 char file_ends_name[MAXNS]; ///文本输出的地址 char AC_TIRE_ARR[325 * 25]; typedef struct NODE { char words[MAXN]; struct NODE *next; int num; ///代表行数 bool flag; ///flag代表行结束 } node; typedef struct TIRE { struct TIRE *next[30]; bool flag; struct TIRE *fail; } tire; node *head; node *creat() { node *p; p = new node; memset(p->words, 0, sizeof(p->words)); p->num = 1; p->flag = false; p->next = NULL; return p; } bool PD1(char word[]) ///判断行是否结束 { int len = strlen(word); if (word[len - 1] == '\n') { return true; } else return false; } int get_hang(char fileopenname[MAXNS], node *head, int move, int &fflag) ///fflag代表是否读到文件尾 { FILE *fp; int re; ///代表字节数 int biaoji = 0; re = 0; fp = fopen(fileopenname, "r"); fseek(fp, move, 0); int number = 1; ///number代表行数 while (fgets(head->words, 81, fp) != NULL) { re += (strlen(head->words) + 1); //printf("%d\n",re); if (PD1(head->words)) { number++; if (number == 21) { fflag = 1; break; } head->flag = true; node *p; p = creat(); p->num = number; head->next = p; head = head->next; } else { node *p; p = creat(); p->num = number; head->next = p; head = head->next; } } fclose(fp); return re; } void PRINTNODE(node *head) ///输出链表 { if (head == NULL) { printf("error,no thing\n"); return; } printf("%4d ", head->num); while (head) { printf("%s", head->words); if (head->flag == true) { //printf("...\n"); int lens = strlen(head->words); if (head->words[lens - 1] != '\n') printf("\n"); ///这个是针对于添加之后的。 if (head->next != NULL) printf("%4d ", head->next->num); } head = head->next; } //puts(""); } void clearl(node *p) { if (p != NULL) { clearl(p->next); free(p); } } void clear() { system("cls"); } void view() { printf("\n"); printf("活区切换。格式:n<回车>\n"); printf("活区显示。格式:p<回车> \n"); printf("行插入。格式:i<行号><回车><文本><回车> \n"); printf("行删除。格式:d<行号 1>[ <行号 2>]<回车> \n"); printf("活区多或单模式匹配。 格式:m<模式串 1>[ <模式串 2> ......]<回车>\n"); } void only_insert(char strs[305], node *&head) { node *hhead; hhead = head; int j = 0; char wordss[325]; int ans; while (hhead->flag == false) { ans = strlen(hhead->words); for (int i = 0; i < ans; i++) { wordss[j] = hhead->words[i]; j++; //printf("...........\n"); } } ans = strlen(hhead->words); for (int i = 0; i < ans; i++) { //printf("%c",hhead->words[i]); wordss[j] = hhead->words[i]; j++; } if (wordss[j - 1] != '\n') wordss[j] = '\n'; FILE *fp = fopen(strs, "a+"); fputs(wordss, fp); fclose(fp); hhead = hhead->next; head = hhead; } void add_hang(int n, node *&head) { int j = 0; node *p; node *begins; node *headss = creat(); node *heads = creat(); begins = creat(); p = creat(); heads = head; headss = head; begins = p; p->num = n + 1; char str[355]; printf("please the words \n"); cin >> str; getchar(); int len = strlen(str); for (int i = 0; i < len; i++) { p->words[j++] = str[i]; if (j == 81) { j = 0; node *q; q = creat(); q->num = n + 1; p->next = q; p = p->next; } } //p->words[j]='\n'; p->flag = true; if (n == 0) { p->next = head; head = p; headss = head; //printf("%s\n",head->words); int numbers = 1; while (headss) { headss->num = numbers; if (headss->flag == true && headss->next != NULL) { numbers++; } headss = headss->next; } //PRINTNODE(head); if (numbers > 20) { only_insert(file_ends_name, head); } node *hhead; hhead = creat(); hhead = head; numbers = 1; while (hhead) { hhead->num = numbers; if (hhead->flag == true) { numbers++; } hhead = hhead->next; } } else { while (headss->next->num != begins->num) { //printf("%d\n",headss->next->num); headss = headss->next; if (headss->next == NULL) { break; } } headss->flag = true; p->next = headss->next; headss->next = begins; int numbers = 1; while (heads) { heads->num = numbers; if (heads->flag == true && heads->next != NULL) { numbers++; } heads = heads->next; } //PRINTNODE(head); if (numbers > 20) { only_insert(file_ends_name, head); } node *headsss; headsss = creat(); headsss = head; numbers = 1; while (headsss) { headsss->num = numbers; if (headsss->flag == true) { numbers++; } headsss = headsss->next; } } } void INPUT_file(char file_sname[MAXNS], node *head) { char buf[MAXN * 10]; memset(buf, 0, sizeof(buf)); FILE *fps = fopen(file_sname, "a+"); int j = 0; while (head) { int len = strlen(head->words); for (int i = 0; i < len; i++) { buf[j] = head->words[i]; j++; } if (head->flag == true && head->next == NULL) { //printf("%s\n",buf); fputs(buf, fps); j = 0; memset(buf, 0, sizeof(buf)); } if (head->flag == false && head->next == NULL) { fputs(buf, fps); j = 0; memset(buf, 0, sizeof(buf)); } if (head->flag == true && head->next != NULL) { int lens = strlen(head->words); if (head->words[lens - 1] != '\n') buf[j] = '\n'; fputs(buf, fps); j = 0; memset(buf, 0, sizeof(buf)); } head = head->next; } fclose(fps); } void Node_clear(node *s, node *h) { while (s != h) { //printf("...........\n"); Node_clear(s->next, h); delete s; } } void del_file_one(int ans1, int ans2, node *&head) ///代表删除一个区间 { node *head1; ///代表指向前一个的指针 head1 = creat(); node *head2; ///代表指向后一个的指针 head2 = creat(); node *head3; ///释放内存空间 head3 = creat(); node *head4; head4 = creat(); head1 = head; head2 = head; ///ans1 ans2 0 10 代表0到10都被删除 if (ans1 != 1) { while (head1->next->num != ans1) { head1 = head1->next; } while (head2->next->num != ans2 + 1) { head2 = head2->next; if (head2->next == NULL) { break; } } head3 = head1->next; head4 = head2; head1->next = head2->next; //Node_clear(head3,head2); /** 以下是一个重新构造输出数据的函数 **/ node *heads; heads = creat(); heads = head; int numbers = 1; while (heads) { heads->num = numbers; if (heads->flag == true) { numbers++; } heads = heads->next; } } else if (ans1 == 1) { while (head2->next->num != ans2 + 1) { head2 = head2->next; if (head2->next == NULL) { break; } } //printf("%s\n",head2->words); head3 = head; head4 = head2; head2 = head2->next; head = head2; //Node_clear(head3,head4); node *headss; headss = creat(); headss = head; int numbers = 1; while (headss) { headss->num = numbers; if (headss->flag == true) { numbers++; } headss = headss->next; } } } void del_file_two(int ans1, node *&head) { if (ans1 == 1) { node *heads; heads = creat(); heads = head; while (heads->next->num == 1) { heads = heads->next; if (heads->next == NULL) { break; } } heads = heads->next; head = heads; node *head1; head1 = creat(); head1 = head; int numbers = 1; while (head1) { head1->num = numbers; if (head1->flag == true) { numbers++; } head1 = head1->next; } } else { int ans2 = ans1 + 1; node *head1; head1 = creat(); head1 = head; node *head2; head2 = creat(); head2 = head; while (head1->next->num != ans1) { head1 = head1->next; if (head1->next == NULL) break; } while (head2->next->num != ans2) { head2 = head2->next; if (head2->next == NULL) { break; } } head2 = head2->next; head1->next = head2; int nums = 1; node *head3; head3 = creat(); head3 = head; while (head3) { head3->num = nums; if (head3->flag == true) nums++; head3 = head3->next; } } } tire *creat_tire() { tire *p; p = new tire; p->flag = false; for (int i = 0; i < 30; i++) { p->next[i] = NULL; } p->fail = NULL; return p; } //tire *root = creat_tire(); void insert_tire(char words[], tire *root) { int len; len = strlen(words); tire *p; p = root; for (int i = 0; i < len; i++) { int ans; ans = words[i] - 'a'; //printf("%d\n",ans); if (p->next[ans] == NULL) { tire *q; q = creat_tire(); p->next[ans] = q; } p = p->next[ans]; } p->flag = true; ///代表结束 } void build_ACtire(tire *root) { tire *p = root; tire *q; queue<tire *> que; for (int i = 0; i < 30; i++) { if (p->next[i] != NULL) { p->next[i]->fail = root; que.push(p->next[i]); } else p->next[i] = root; } while (!que.empty()) { tire *to; to = que.front(); que.pop(); for (int i = 0; i < 30; i++) { ///因为第一个模式串如果匹配失败,有可能是另一个匹配串 ///所以应该找到另一个 ///失配的话,就是回到root点再寻找 if (to->next[i] != NULL) { to->next[i]->fail = to->fail->next[i]; que.push(to->next[i]); } else to->next[i] = to->fail->next[i]; } } } bool ACTIRE_search(char str[], tire *root) { vector<int> vec; int len = strlen(str); //printf("%d\n",len); tire *ans; ans = creat_tire(); ans = root; for (int i = 0; i < len; i++) { int num = str[i] - 'a'; if (num < 0) continue; //printf("%d...\n",num); if (ans->next[num] != NULL) { ans = ans->next[num]; //printf("%d....\n",ans->flag); if (ans->flag == true) { //if(ans==NULL) //printf("............................................\n"); vec.push_back(i); } } else { if (ans == root) i++; else { ans = ans->fail; if (ans->flag == true) { vec.push_back(i); } } } } if (vec.size() != 0) { for (int i = 0; i < vec.size(); i++) { printf("%d ", vec[i]); } puts(""); return true; } return false; } int main() { int file_move = 0; node *p; int end_flag = 0; printf("please cin the in file_name\n"); cin >> file_name; getchar(); printf("please cin the out file_name\n"); cin >> file_ends_name; getchar(); while (1) { view(); node *head; char s[100]; char strs[105]; int nums; gets(s); if (s[0] == 'n') { if (file_move == 0) { head = creat(); nums = get_hang(file_name, head, file_move, end_flag); if (nums == 0) { printf("error\n"); continue; } file_move += nums; PRINTNODE(head); } else { INPUT_file(file_ends_name, head); head = creat(); nums = get_hang(file_name, head, file_move, end_flag); if (nums == 0) { printf("the file open is error\n"); printf("because the file is end\n"); continue; } file_move += nums; PRINTNODE(head); } } if (s[0] == 'p') { PRINTNODE(head); } if (s[0] == 'i') { int ans = 0; int len; len = strlen(s); for (int i = 1; i < len; i++) { if (s[i] >= '0' && s[i] <= '9') { ans = ans * 10 + (s[i] - '0'); } else break; } add_hang(ans, head); } if (s[0] == 'd') { int ans1, ans2; ans1 = 0; ans2 = 0; int i; int len = strlen(s); for (i = 1; i < len; i++) { if (s[i] >= '0' && s[i] <= '9') { ans1 = ans1 * 10 + (s[i] - '0'); } else break; } if (i == len) { del_file_two(ans1, head); } else { for (i = i + 1; i < len; i++) { if (s[i] >= '0' && s[i] <= '9') { ans2 = ans2 * 10 + (s[i] - '0'); } else break; } del_file_one(ans1, ans2, head); } } if (s[0] == 'm') { tire *root = creat_tire(); root = new tire; root->flag = false; for (int i = 0; i < 30; i++) root->next[i] = NULL; root->fail = NULL; char hzb[MAXN * 2]; memset(hzb, 0, sizeof(hzb)); int hzblen = strlen(s); int hzbj = 0; for (int i = 1; i < hzblen; i++) { if (s[i] == ' ') { hzb[hzbj] = '\0'; //printf("%s\n", hzb); insert_tire(hzb, root); hzbj = 0; continue; } hzb[hzbj++] = s[i]; } hzb[hzbj] = '\0'; //printf("%s\n", hzb); insert_tire(hzb, root); build_ACtire(root); node *hzb_head; hzb_head = creat(); hzb_head = head; memset(AC_TIRE_ARR, 0, sizeof(AC_TIRE_ARR)); int hzbz = 0; while (hzb_head) { int hhzb = strlen(hzb_head->words); for (int i = 0; i < hhzb; i++) if (hzb_head->words[i] != '\n') AC_TIRE_ARR[hzbz++] = hzb_head->words[i]; hzb_head = hzb_head->next; } AC_TIRE_ARR[hzbz] = '\0'; bool hflag = ACTIRE_search(AC_TIRE_ARR, root); if (hflag == false) printf("sorry!this is nothing\n"); } } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用