C語言實(shí)現(xiàn)簡易文本編輯器
本程序要求完成一個(gè)簡易文本編輯器,能夠完成文本的錄入、編輯、刪除、查找,并能夠完成文件的存取。
在文本編輯軟件中把用戶輸入的所有文本內(nèi)容作為一個(gè)字符串。雖然各種文本編輯軟件的功能有強(qiáng)弱差別,但是基本操作都包括串的輸入、修改、刪除(包括整行刪除和一行中的子串刪除)、查找、輸出等。通過分析,系統(tǒng)應(yīng)該包括以下功能:
1、具有簡單的文字或圖形菜單界面
2、能實(shí)現(xiàn)串或文本塊的查找、替換、刪除、插入、移動操作。
3、能實(shí)現(xiàn)文本文件的存盤和讀取功能。
4、具有友好的界面和較強(qiáng)的容錯(cuò)能力
設(shè)計(jì)思路
1、采用的邏輯結(jié)構(gòu)
文本編輯器主要是針對文本進(jìn)行編輯,文本的操作就是對字符的操作。文本編輯器可以從行、列兩個(gè)方向進(jìn)行編輯。
每一行可以看成一個(gè)線性表,線性表是一種線性結(jié)構(gòu),線性結(jié)構(gòu)的特點(diǎn)是數(shù)據(jù)元素之間為線性關(guān)系,數(shù)據(jù)元素“一個(gè)接一個(gè)的排列”。在一個(gè)線性表中數(shù)據(jù)元素的類型是相同的,由于每一行可以存儲的最大字?jǐn)?shù)是相同的,行方向所有線性表的最大長度可以設(shè)置成相同的。行與行之間的關(guān)系也可以看成一個(gè)線性表。
2、采用的存儲結(jié)構(gòu)
線性表的存儲分為兩種:順序存儲和鏈?zhǔn)酱鎯Α?/p>
順序存儲是指在內(nèi)存中用地址連續(xù)的一塊存儲空間順序存放線性表的各元素,用這種存儲形式存儲的線性表稱為順序表。在程序設(shè)計(jì)語言中,一維數(shù)組在內(nèi)存中占用的存儲空間就是一組連續(xù)的存儲區(qū)域,因此,用一維數(shù)組來表示順序表的數(shù)據(jù)存儲區(qū)域是再合適不過的。
鏈?zhǔn)酱鎯κ峭ㄟ^-組任意的存儲單元來存儲線性表中的數(shù)據(jù)元素的,為建立數(shù)據(jù)元系之間的線性關(guān)系,對每個(gè)數(shù)據(jù)元素除了存放數(shù)據(jù)元素自身的信息之外,還需要和一起存放其后繼或前驅(qū)所在的存儲單元的地址,這兩部分信息組成一個(gè)“結(jié)點(diǎn)”,每個(gè)元素都如此。存放數(shù)據(jù)元素信息的稱為數(shù)據(jù)域,存放其前驅(qū)或后繼地址的稱為指針域。只有一個(gè)存儲單元地址的為單鏈表,有兩個(gè)存儲單元地址的為雙鏈表。
考慮到實(shí)際的功能需求,每行的線性表可以用順序存儲方式,每個(gè)字符是一個(gè)節(jié)點(diǎn)。用數(shù)組的長度表示本行可以輸入的最大字符。行與行之間的線性表采用雙鏈表存儲,每個(gè)節(jié)點(diǎn)包括四個(gè)區(qū)域,一個(gè)指針域prior指向上一行,一個(gè)指針域next指向下一行,一個(gè)數(shù)據(jù)域num是行號,一個(gè)數(shù)據(jù)域是本行的字符數(shù)組。程序以行和列標(biāo)識文本位置,行采用雙向鏈表存儲行信息,用數(shù)組下標(biāo)標(biāo)識列信息,從而能夠準(zhǔn)確定位字符位置,然后進(jìn)行查找、替換、插入、塊移動、刪除等多種操作。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX_LEN 100
#define NOT_FOUND -1
//定義行結(jié)構(gòu)體:
struct line
{
char text[MAX_LEN]; //本行文本
int num; //行號
struct line *next; //指向下一個(gè)行的指針
struct line *prior; //指向前一個(gè)行的指針
};
int lnum;
struct line *start; //指向線性表中第一行的指針
struct line *last; //指向線性表中最后一行的指針
struct line *find(int); //查找指定行是否存在
void patchup(int, int); //對當(dāng)前行以后的每行的行號加1或
void delete_text(int); //刪除一行文字
void list(); //顯示文件的全部內(nèi)容
void save(); //保存文件
void load(); //打開文件,初始化線性表
void insert(char str[], int linenum, int position); //插入文字到一行的中間
void printline(int linenum); //打印一行文字
void deletestr(int linenum, int position, int lenth); //刪除一個(gè)字符串
int findstr(char * to_find); //查找字符串
int menu_select(); //顯示主菜單
int menu_select_insert();//顯示插入功能子菜單
int menu_select_delete();//顯示刪除功能子菜單
int menu_select_print(); //顯示打印功能子菜單
int menu_select_move(); //顯示移動功能子菜單
void enter(int linenum); //插入一行文字
void enter_empty(int linenum); //插入一個(gè)空白行
//下列函數(shù)是系統(tǒng)主函數(shù),提供系統(tǒng)主界面,通過選擇項(xiàng)轉(zhuǎn)入執(zhí)行插入、刪除、查存盤、讀人文件等功能的界面。
int main(void)
{
char str[MAX_LEN];
int choice;
int linenum = 1;
int number = 0;
start = NULL;
last = NULL;
load(); //打開文件,初始化線性表
do{
choice = menu_select();
switch (choice)
{
case 1: //執(zhí)行插入功能
choice = menu_select_insert();//顯示插入子菜單
switch (choice)
{
case 1: //插入一行
printf("\t行號:");
scanf("%d", &linenum);
enter(linenum);
break;
case 2: //插入到指定行的指定列
printf("輸入插入位置一行號:");
scanf("%d", &linenum);
printf("輸入插入位置-列號:");
scanf("%d", &number);
printf("要插入的字符串:");
scanf("%s", str);
insert(str, linenum, number);
break;
case 3: //退出插入
break;
}
break;
case 2: //執(zhí)行刪除功能
choice = menu_select_delete(); // 刪除子菜單
switch (choice)
{
case 1: //刪除指定行
printf("\t行號:");
scanf("%d", &linenum);
break;
case 2: //刪除指定的字符串
printf("要?jiǎng)h除的字符串:");
scanf("%s", str);
number = findstr(str);
if (number == NOT_FOUND)
printf("沒有找到");
else
deletestr(lnum, number, strlen(str));
break;
case 3: //退出刪除
break;
}
break;
case 3: //執(zhí)行顯示功能
choice = menu_select_print(); //顯示子菜單
switch (choice) //顯示指定行
{
case 1:
printf("\t行號:");
scanf("%d", &linenum);
printline(linenum);
break;
case 2: //顯示全部
list();
break;
case 3: //退出顯示
break;
}
break;
case 4: //執(zhí)行查找功能
printf("輸入想要查找的字符串:");
scanf("%s", str);
number = findstr(str);
if (number == NOT_FOUND)
printf("沒有找到");
else
printf("要查找的字符串所在行號:%d,列號:%d\n", lnum, number + 1);
break;
case 5: //執(zhí)行替換功能
printf("輸入被替換的字符串:");
scanf("%s", str);
number = findstr(str);
if (number == NOT_FOUND)
printf("沒有找到");
else
{
deletestr(lnum, number, strlen(str));
printf("要替換的字符串:");
scanf("%s", str);
insert(str, lnum, number + 1);
}
break;
case 6: //執(zhí)行移動功能
choice = menu_select_move(); //移動子菜單
switch (choice)
{
case 1: // 向下移動一行
printf("輸人要移動的字符串所在行號:");
scanf("%d", &linenum);
enter_empty(linenum);
break;
case 2: //向上移動一行
printf("輸入要移動的字符串所在行號:");
scanf("%d", &linenum);
delete_text(linenum - 1);
break;
case 3: //向右移動一列
printf("輸人要移動的字符串所在行號:");
scanf("%d", &linenum);
printf("輸入要移動的字符串所在列號:");
scanf("%d", &number);
str[0] = ' ';
str[1] = '\0';
insert(str, linenum, number);
break;
case 4: //向左移動
printf("輸入要移動的字符串所在行號:");
scanf("%d", &linenum);
printf("輸入要移動的字符串所在列號:");
scanf("%d", &number);
if (number <= 0)
printf("該列不存在");
else
deletestr(linenum, number - 2, 1);
break;
case 5: //退出移動
break;
}
break;
case 7: //執(zhí)行存盤功能
save();
break;
case 8: //執(zhí)行讀入文件功能
load();
break;
case 9: //執(zhí)行退出功能
exit(0);
break;
}
} while (1);
return 0;
}
//下列函數(shù)是主菜單功能的提示界面,其功能是說明主菜單中選項(xiàng)
int menu_select()
{
int c;
printf("\n\t\t1.插入\n");
printf("\t\t2.刪除\n");
printf("\t\t3.顯示\n");
printf("\t\t4.查找\n");
printf("\t\t5.替換\n");
printf("\t\t6.移動\n");
printf("\t\t7.文件存盤\n");
printf("\t\t8.裝入文件\n");
printf("\t\t9.退出\n");
do
{
printf("\n\n\t\t請按數(shù)字選擇:");
scanf("%d", &c);
} while (!(c >= 1 && c <= 9));
return(c);
}
//下列函數(shù)是插入子菜單功能的提示界面,其功能是說明在插入菜單下選項(xiàng)的含義。
int menu_select_insert()
{
int c;
printf("\n\t\t1.插入一行文字\n");
printf("\t\t2.插入一段文字\n");
printf("\t\t3.返回上級菜單\n");
do{
printf("\n\n\t\t請按數(shù)字選擇:");
scanf("%d", &c);
} while (!(c >= 1 && c <= 3));
return(c);
}
//下列函數(shù)是刪除子菜單功能的提示界面,其功能是說明在刪除子菜單下選項(xiàng)的含義。
int menu_select_delete()
{
int c;
printf("\n\t\t1.刪除一行文字\n");
printf("\t\t2.刪除一段文字\n");
printf("\t\t3.返回上級菜單\n");
do{
printf("\n\n\t\t請按數(shù)字選擇:");
scanf("%d", &c);
} while (!(c >= 1 && c <= 3));
return(c);
}
//下列函數(shù)是顯示子菜單功能的提示界面,其功能是說明在顯示子菜單下選項(xiàng)的含義
int menu_select_print()
{
int c;
printf("\n\t\t1.顯示一行\(zhòng)n");
printf("\t\t2.全部顯示\n");
printf("\t\t3.返回上級菜單\n");
do{
printf("\n\n\t\t請按數(shù)字選擇:");
scanf("%d", &c);
}while(!(c >= 1 && c <= 3));
return(c);
}
//下列函數(shù)是移動子菜單功能的提示界面,其功能是說明在移動子菜單下選項(xiàng)的含義
int menu_select_move()
{
int c;
printf("\n\t\t1.向下移動一行\(zhòng)n");
printf("\t\t2.向上移動一行\(zhòng)n");
printf("\t\t3.向右移動一列\(zhòng)n");
printf("\t\t4.向左移動一列\(zhòng)n");
printf("\t\t5.返回上級菜單\n");
do{
printf("\n\n\t\t請按數(shù)字選擇:");
scanf("%d", &c);
} while (!(c >= 1 && c <= 5));
return(c);
}
//下列函數(shù)的功能是在指定的行號 linenum處插入一行文字。
void enter(int linenum)
{
struct line * info, * q, * p;
p = start;
q = NULL;
while (p && p->num != linenum) //找到插入行
{
q = p;
p = p->next;
}
if (p == NULL && (q->num + 1) != linenum) //指定行不存在,不能插入
{
printf("輸入的行號不存在");
}
else // 指定行存在,進(jìn)行插入
{
info = (struct line *)malloc(sizeof(struct line));
printf("輸入要輸入的字符串");
scanf("%s", info->text);
info->num = linenum;
if (linenum == 1) //插入在第一行
{
info->next = p;
p->prior = info;
info->prior = NULL;
start = info;
}
else if (q->num != linenum) //插入在最后一行
{
q->next = info;
info->next = p;
info->prior = q;
}
else //插入在其他行
{
q->next = info;
info->next = p;
p->prior = info;
info->prior = q;
}
while (p) //如果不是插入在最后一行,插入行后面的行號都加1
{
p->num = p->num + 1;
p = p->next;
}
}
}
//下列函數(shù)是為其他功能提供的一個(gè)輔助函數(shù),它的功能是當(dāng)文本內(nèi)容插在文件中間時(shí)
//其下面的內(nèi)容的行號必須增加1,而刪除時(shí),被刪除的文本后面的行號必減1.
void patchup(int n, int incr)
{
struct line *i;
i = find(n);
i = i->next;
while (i)
{
i->num = i->num + incr;
i = i->next;
}
}
//下列函數(shù)的功能是在指定行處插入一個(gè)空白行。
void enter_empty(int linenum)
{
struct line *info, *p;
info = (struct line *)malloc(sizeof(struct line));
if (!info)
{
printf("\t!內(nèi)存不夠!\n");
exit(0);
}
info->text[0] = ' ';
info->text[1] = '\0';
info->num = linenum;
if (find(linenum)) //如果要插人的行號存在,則進(jìn)行插入
{
p = start;
if (linenum == 1) //插入在首行
{
info->next = p;
start = info;
info->prior = NULL;
p->prior = info;
}
else //插入在其他行
{
while (p->next->num != linenum)
p = p->next;
info->next = p->next;
p->next->prior = info;
p->next = info;
info->prior = p;
}
patchup(linenum, 1);
}
else
printf("該行不存在");
}
//下列函數(shù)的功能是插入文字到一行的中間。要是插入位置和現(xiàn)有位置中間有間隔,會補(bǔ)全空格
void insert(char str[], int linenum, int position)
{
struct line * info;
int len, i;
int lenth;
char rest_str[MAX_LEN], nostr[2] = { " " };
info = start;
while (info && info->num != linenum) //查詢要插入的行
{
info = info->next;
}
if (info == NULL)
printf("不存在該行!\n");
else if (position < 0)
printf("不存在該列!\n");
else //如果行和列都存在,則進(jìn)行插入
{
lenth = strlen(info->text);
if (lenth < position) //插入列大于本行文件列數(shù)
{
len = position - lenth - 1;
for (i = 0; i < len; i++)
strcat(info->text, nostr); //將空余的部分插入空格符
strcat(info->text, str); //插入字符到列的未尾
}
else //插入列在本行文字的中間
{
strcpy(rest_str, &info->text[position - 1]);
strcpy(&info->text[position - 1], str);
strcat(info->text, rest_str);
}
}
}
//下列函數(shù)的功能是刪除指定行、指定位置、長度為 lenth的一段文字。
void deletestr(int linenum, int position, int lenth)
{
struct line * info;
char rest_str[MAX_LEN];
info = find(linenum);
if (info == NULL)
printf("該行沒有字符!n");
else
{
if (strlen(info->text) <= (position + lenth)) //本行的字符長度<=待刪除的列號+刪除長度,直接在當(dāng)前位置插入'\0'
info->text[position] = '\0';
else
{
strcpy(rest_str, &info->text[position + lenth]);
strcpy(&info->text[position], rest_str);
}
}
}
//下列函數(shù)的功能是刪除指定行號 lineup的文字。
void delete_text(int linenum)
{
struct line * info, *p;
info = start;
while ((info->num < linenum) && info)
info = info->next;
if (info->next == NULL)
printf("該行不存在");
else
{
p = info->next;
if (start == info) //如果刪除的是第一行
{
start = info->next;
if (start) //如果刪除后,不為空
start->prior = NULL;
else //刪除后為空
last = NULL;
}
else
{
info->prior->next = info->next; //指定行的上一行指向指定行的下一行
if (info != last) //如果不是最后一行
info->next->prior = info->prior; //修改其下一行的指向頭的指針
else //如果是最后一行,修改尾指針
last = info->prior;
}
free(info);
while (p)
{
p->num = p->num - 1;
p = p->next;
}
}
}
//下列函數(shù)的功能是查找一段文字。
int findstr(char * to_find)
{
struct line * info;
int i = 0, find_len, found = 0, position;
char substring[MAX_LEN];
info = start;
lnum = 0; //匹配到的行號
find_len = strlen(to_find);
while (info && !found) //查詢
{
i = 0; //行間循環(huán)
while (!found && (i <= strlen(info->text) - find_len)) //行內(nèi)查找循環(huán)
{
strcpy(substring, &info->text[i], find_len);
substring[find_len] = '\0';
if (strcmp(substring, to_find) == 0)
{
found = 1;
lnum = info->num;
}
else
++i;
}
info = info->next;
}
if (found) //查找成功
position = i;
else //查找不成功
position = NOT_FOUND;
return(position);
}
//下列函數(shù)的功能是查找指定行,如果查找成功返回結(jié)點(diǎn)所在的行指針。
struct line * find(int linenum)
{
struct line * info;
info = start;
while (info)
{
if (linenum != info->num)
info = info->next;
else
break;
}
return (info);
}
//下列函數(shù)的功能是顯示指定行
void printline(int linenum)
{
struct line *info;
info = find(linenum);
if (info)
printf("%d:%s\n", info->num, info->text);
else
printf("該行不存在");
}
//下列函數(shù)的功能是顯示線性表中的所有文本
void list()
{
struct line * info;
info = start;
while (info)
{
printf("%d:%s\n", info->num, info->text);
info = info->next;
}
printf("\n\n");
}
//下列函數(shù)的功能是把線性表中的所有文字保存到文件中
void save()
{
struct line * info;
char * p;
FILE * fp;
if ((fp = fopen("D:\\text.txt", "w")) == NULL){
printf("\t文件打不開!n");
exit(0);
}
printf("\t正在存入文件!\n");
info = start;
while (info)
{
p = info->text;
while (*p)
putc(*p++, fp);
putc('\n', fp);
info = info->next;
}
fclose(fp);
}
//下列函數(shù)的功能是把文本文件中的內(nèi)容讀入到線性表中。
void load()
{
struct line *info, *temp; //info指向當(dāng)前行,temp指向info的前驅(qū)行
char c;
FILE *fp; //文件指針
int inct, i; //行計(jì)數(shù)器
temp = NULL;
if ((fp = fopen("D:\\text.txt", "r")) == NULL)
{
printf("\t文件打不開!\n");
exit(0);
}
printf("\n\t正裝入文件!\n");
start = (struct line*)malloc(sizeof(struct line)); //動態(tài)生成一行的結(jié)點(diǎn)空間
info = start;
inct = 1;
while ((c = fgetc(fp)) != EOF)
{
i = 0;
info->text[i] = c;
i++;
while ((c = fgetc(fp)) != '\n') //從文件中讀取一行字符到線性表中,文件中每一行以\n為結(jié)束標(biāo)
{
info->text[i] = c;
i++;
}
info->text[i] = '\0'; //線性表中每行末尾的結(jié)束標(biāo)志
info->num = inct++; //行號和計(jì)數(shù)器都加1
info->next = (struct line*)malloc(sizeof(struct line));
if (!info->next)
{
printf("\n\t內(nèi)存已經(jīng)用完!");
exit(0);
}
info->prior = temp;
temp = info;
info = info->next;
}
temp->next = NULL;
last = temp;
free(info);
start->prior = NULL;
fclose(fp);
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C語言如何計(jì)算兩個(gè)數(shù)的最小公倍數(shù)
這篇文章主要介紹了C語言如何計(jì)算兩個(gè)數(shù)的最小公倍數(shù),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
vs code 配置c/c++環(huán)境的詳細(xì)教程(推薦)
這篇文章主要介紹了vs code 配置c/c++環(huán)境的詳細(xì)教程(推薦),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11
C++實(shí)現(xiàn)LeetCode(21.混合插入有序鏈表)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(21.混合插入有序鏈表),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C++實(shí)現(xiàn)職工工資管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡單的職工工資管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
深入學(xué)習(xí)C語言mmap和shm*的使用方法技巧
本文將詳細(xì)介紹mmap和shm的工作原理,包括它們在內(nèi)存映射和共享內(nèi)存方面的優(yōu)勢和適用場景,同時(shí),文章還會分享一些使用mmap和shm的技巧和經(jīng)驗(yàn),以幫助讀者優(yōu)化并提高程序性能,使你能夠在實(shí)際項(xiàng)目中更好地利用這些技術(shù)來加速數(shù)據(jù)共享和多線程應(yīng)用2023-10-10

