实验题目:简单行编辑程序
[问题描述]
文本编辑程序是利用计算机进行文字加工的基本软件工具,实现对文本文件的插入、删除等修改操作。限制这些操作以行为单位进行的编辑程序称为行编辑程序。
被编辑的文本文件可能很大,全部读入编辑程序的数据空间(内存)的做法既不经济,
也不总能实现。一种解决方法是逐段地编辑。任何时刻只把待编辑文件的一段放在内存,称为活区。试按照这种方法实现一个简单的行编辑程序。设文件每行不超过 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)储存结构:
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;
}
转载于:https://www.cnblogs.com/qq136155330/p/9231565.html