C語言實現簡易文本編輯器

本程序要求完成一個簡易文本編輯器,能夠完成文本的錄入、編輯、刪除、查找,並能夠完成文件的存取。

在文本編輯軟件中把用戶輸入的所有文本內容作為一個字符串。雖然各種文本編輯軟件的功能有強弱差別,但是基本操作都包括串的輸入、修改、刪除(包括整行刪除和一行中的子串刪除)、查找、輸出等。通過分析,系統應該包括以下功能:

1、具有簡單的文字或圖形菜單界面
2、能實現串或文本塊的查找、替換、刪除、插入、移動操作。
3、能實現文本文件的存盤和讀取功能。
4、具有友好的界面和較強的容錯能力

設計思路

1、采用的邏輯結構

文本編輯器主要是針對文本進行編輯,文本的操作就是對字符的操作。文本編輯器可以從行、列兩個方向進行編輯。

每一行可以看成一個線性表,線性表是一種線性結構,線性結構的特點是數據元素之間為線性關系,數據元素“一個接一個的排列”。在一個線性表中數據元素的類型是相同的,由於每一行可以存儲的最大字數是相同的,行方向所有線性表的最大長度可以設置成相同的。行與行之間的關系也可以看成一個線性表。

2、采用的存儲結構

線性表的存儲分為兩種:順序存儲和鏈式存儲。

順序存儲是指在內存中用地址連續的一塊存儲空間順序存放線性表的各元素,用這種存儲形式存儲的線性表稱為順序表。在程序設計語言中,一維數組在內存中占用的存儲空間就是一組連續的存儲區域,因此,用一維數組來表示順序表的數據存儲區域是再合適不過的。

鏈式存儲是通過-組任意的存儲單元來存儲線性表中的數據元素的,為建立數據元系之間的線性關系,對每個數據元素除瞭存放數據元素自身的信息之外,還需要和一起存放其後繼或前驅所在的存儲單元的地址,這兩部分信息組成一個“結點”,每個元素都如此。存放數據元素信息的稱為數據域,存放其前驅或後繼地址的稱為指針域。隻有一個存儲單元地址的為單鏈表,有兩個存儲單元地址的為雙鏈表。

考慮到實際的功能需求,每行的線性表可以用順序存儲方式,每個字符是一個節點。用數組的長度表示本行可以輸入的最大字符。行與行之間的線性表采用雙鏈表存儲,每個節點包括四個區域,一個指針域prior指向上一行,一個指針域next指向下一行,一個數據域num是行號,一個數據域是本行的字符數組。程序以行和列標識文本位置,行采用雙向鏈表存儲行信息,用數組下標標識列信息,從而能夠準確定位字符位置,然後進行查找、替換、插入、塊移動、刪除等多種操作。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX_LEN 100
#define NOT_FOUND -1
//定義行結構體:
struct line
{
 char text[MAX_LEN];  //本行文本
 int num;  //行號
 struct line *next; //指向下一個行的指針
 struct line *prior; //指向前一個行的指針
};
int lnum;
struct line *start;     //指向線性表中第一行的指針
struct line *last;     //指向線性表中最後一行的指針
struct line *find(int);    //查找指定行是否存在
void patchup(int, int);    //對當前行以後的每行的行號加1或
void delete_text(int);     //刪除一行文字
void list();   //顯示文件的全部內容
void save();   //保存文件
void load();   //打開文件,初始化線性表
void insert(char str[], int linenum, int position); //插入文字到一行的中間
void printline(int linenum);   //打印一行文字
void deletestr(int linenum, int position, int lenth); //刪除一個字符串
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); //插入一個空白行
//下列函數是系統主函數,提供系統主界面,通過選擇項轉入執行插入、刪除、查存盤、讀人文件等功能的界面。
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:  //執行插入功能 
   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:   //執行刪除功能
   choice = menu_select_delete();  // 刪除子菜單
   switch (choice)
   {
   case 1:   //刪除指定行
    printf("\t行號:");
    scanf("%d", &linenum);
    break;
   case 2:   //刪除指定的字符串
    printf("要刪除的字符串:");
    scanf("%s", str);
    number = findstr(str);
    if (number == NOT_FOUND)
     printf("沒有找到");
    else
     deletestr(lnum, number, strlen(str));
    break;
   case 3:  //退出刪除
    break;
   }
   break;
  case 3:   //執行顯示功能
   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:   //執行查找功能
   printf("輸入想要查找的字符串:");
   scanf("%s", str);
   number = findstr(str);
   if (number == NOT_FOUND)
    printf("沒有找到");
   else
    printf("要查找的字符串所在行號:%d,列號:%d\n", lnum, number + 1);
   break;
  case 5:  //執行替換功能
   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:     //執行移動功能
   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:  //執行存盤功能
   save();
   break;
  case 8:  //執行讀入文件功能
   load();
   break;
  case 9:  //執行退出功能
   exit(0);
   break;
  }
 } while (1);
 return 0;
}
//下列函數是主菜單功能的提示界面,其功能是說明主菜單中選項
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請按數字選擇:");
  scanf("%d", &c);
 } while (!(c >= 1 && c <= 9));
 return(c);
}
 
//下列函數是插入子菜單功能的提示界面,其功能是說明在插入菜單下選項的含義。
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請按數字選擇:");
  scanf("%d", &c);
 } while (!(c >= 1 && c <= 3));
 return(c);
}
//下列函數是刪除子菜單功能的提示界面,其功能是說明在刪除子菜單下選項的含義。
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請按數字選擇:");
  scanf("%d", &c);
 } while (!(c >= 1 && c <= 3));
 return(c);
}
//下列函數是顯示子菜單功能的提示界面,其功能是說明在顯示子菜單下選項的含義
int menu_select_print()
{
 int c;
 printf("\n\t\t1.顯示一行\n");
 printf("\t\t2.全部顯示\n");
 printf("\t\t3.返回上級菜單\n");
 do{
  printf("\n\n\t\t請按數字選擇:");
  scanf("%d", &c);
 }while(!(c >= 1 && c <= 3));
 return(c);
}
//下列函數是移動子菜單功能的提示界面,其功能是說明在移動子菜單下選項的含義
int menu_select_move()
{
 int c;
 printf("\n\t\t1.向下移動一行\n");
 printf("\t\t2.向上移動一行\n");
 printf("\t\t3.向右移動一列\n");
 printf("\t\t4.向左移動一列\n");
 printf("\t\t5.返回上級菜單\n");
 do{
  printf("\n\n\t\t請按數字選擇:");
  scanf("%d", &c);
 } while (!(c >= 1 && c <= 5));
 return(c);
}
//下列函數的功能是在指定的行號 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 // 指定行存在,進行插入
 {
  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;
  }
 }
}
 
//下列函數是為其他功能提供的一個輔助函數,它的功能是當文本內容插在文件中間時
//其下面的內容的行號必須增加1,而刪除時,被刪除的文本後面的行號必減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;
 }
}
//下列函數的功能是在指定行處插入一個空白行。
void enter_empty(int linenum)
{
 struct line *info, *p;
 info = (struct line *)malloc(sizeof(struct line));
 if (!info)
 {
  printf("\t!內存不夠!\n");
  exit(0);
 }
 info->text[0] = ' ';
 info->text[1] = '\0';
 info->num = linenum;
 if (find(linenum))    //如果要插人的行號存在,則進行插入
 {
  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("該行不存在");
}
 
//下列函數的功能是插入文字到一行的中間。要是插入位置和現有位置中間有間隔,會補全空格
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    //如果行和列都存在,則進行插入
 {
  lenth = strlen(info->text);
  if (lenth < position) //插入列大於本行文件列數
  {
   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);
  }
 }
}
 
//下列函數的功能是刪除指定行、指定位置、長度為 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))  //本行的字符長度<=待刪除的列號+刪除長度,直接在當前位置插入'\0'
   info->text[position] = '\0';
  else
  {
   strcpy(rest_str, &info->text[position + lenth]);
   strcpy(&info->text[position], rest_str);
  }
 }
}
//下列函數的功能是刪除指定行號 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;
  }
 }
}
//下列函數的功能是查找一段文字。
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;  //行間循環
  while (!found && (i <= strlen(info->text) - find_len))  //行內查找循環
  {
   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);
}
//下列函數的功能是查找指定行,如果查找成功返回結點所在的行指針。
struct line * find(int linenum)
{
 struct line * info;
 info = start;
 while (info)
 {
  if (linenum != info->num)
   info = info->next;
  else
   break;
 }
 return (info);
}
 
//下列函數的功能是顯示指定行
void printline(int linenum)
{
 struct line *info;
 info = find(linenum);
 if (info)
  printf("%d:%s\n", info->num, info->text);
 else
  printf("該行不存在");
}
//下列函數的功能是顯示線性表中的所有文本
void list()
{
 struct line * info;
 info = start;
 while (info)
 {
  printf("%d:%s\n", info->num, info->text);
  info = info->next;
 }
 printf("\n\n");
}
//下列函數的功能是把線性表中的所有文字保存到文件中
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);
}
//下列函數的功能是把文本文件中的內容讀入到線性表中。
void load()
{
 struct line *info, *temp;  //info指向當前行,temp指向info的前驅行
 char c;
 FILE *fp;  //文件指針
 int inct, i;  //行計數器
 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)); //動態生成一行的結點空間
 info = start;
 inct = 1;
 while ((c = fgetc(fp)) != EOF)
 {
  i = 0;
  info->text[i] = c;
  i++;
  while ((c = fgetc(fp)) != '\n')  //從文件中讀取一行字符到線性表中,文件中每一行以\n為結束標
  {
   info->text[i] = c;
   i++;
  }
  info->text[i] = '\0';  //線性表中每行末尾的結束標志
  info->num = inct++;  //行號和計數器都加1
  info->next = (struct line*)malloc(sizeof(struct line));
  if (!info->next)
  {
   printf("\n\t內存已經用完!");
   exit(0);
  }
  info->prior = temp;
  temp = info;
  info = info->next;
 }
 temp->next = NULL;
 last = temp;
 free(info);
 start->prior = NULL;
 fclose(fp);
}

以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: