本課程旨在掌握文本編輯的基本原理與方法,重點實作簡單的行編輯程式,難點在於字串的儲存管理與高效操作。透過堆分配儲存表示,實現動態記憶體管理,支援大型文本的編輯需求。
採用堆動態分配方式儲存字串,結構包含字元指標與長度資訊,支援字串的動態增刪修改:
typedef struct {
char *ch; // 字串基址
int length; // 字串長度
} HString;
此方式避免了靜態陣列的固定長度限制,適合文本編輯器頻繁插入刪除的場景。
對使用者而言,文本由頁、行、字元組成;對程式而言,可視為一個長字串(文本串),頁是其子串,行是頁的子串。為簡化實作,通常將文本簡化為行的集合。
所有文本編輯器的基本操作一致,包含三大核心:
以 C 程式碼為例,原始碼在記憶體中以連續字元形式儲存,換行符 \n 分隔行:
main() { \n
float a,b,max; \n
scanf("%f,%f",&a,&b); \n
if (a>b) max=a; \n
else max=b; \n
} \n
編輯時需維護頁指標、行指標、字元指標,並建立頁表、行表以利快速定位。
以下為完整的行編輯器原始碼,採用順序表管理行結構,支援檔案讀寫、游標移動、字元插入刪除、行新增刪除等功能:
struct line {
int charnum; // 行字元數
char *base; // 行內容指標
};
typedef struct line SequListElemType;
#include "sequlist.h" // 順序表操作介面
#define MAXLINES 1000
SequList *Document; // 文檔行順序表
editline()讀取指定行內容至緩衝區,呼叫 getstring() 進行互動編輯,完成後更新順序表:
void editline(int curline, int maxcolnum) {
char s[2000];
strcpy(s, Document->elem[curline].base);
getstring(editwinx, editwiny+curline-curscrtopline, s, maxcolnum);
free(Document->elem[curline].base);
Document->elem[curline].base = (char *)malloc(strlen(s)+1);
strcpy(Document->elem[curline].base, s);
}
printdoc()在編輯視窗中顯示指定範圍的行內容:
void printdoc(int startfileline, int startscrline) {
int i = startscrline, j = startfileline;
while (i <= editwinlines && i < Document->length) {
clearscrline(editwinx, editwiny+i, 78);
putsxy(editwinx, editwiny+i++, Document->elem[j++-1].base);
}
while (i <= editwinlines) clearscrline(editwinx, editwiny+i++, 78);
}
在字串指定位置插入/刪除字元,需移動後續字元:
void insertchar(char *s, char c, int pos) {
for (int i = strlen(s)+1; i > pos; i--) s[i] = s[i-1];
s[pos] = c;
}
void deletechar(char *s, int pos) {
for (int i = pos; i <= strlen(s); i++) s[i] = s[i+1];
}
getstring()處理鍵盤輸入,支援游標移動、Home/End、Backspace/Delete、插入字元等編輯鍵:
void getstring(int x, int y, char *s, int num) {
int curpos = 0, thisover = 0, k;
while (!thisover) {
if (k = bioskey(1)) {
switch(k) {
case KEYLeft: if (curpos) curpos--; break;
case KEYRight: if (curpos < num) curpos++; break;
case KEYBackspace:
if (curpos) { deletechar(s, curpos-1); curpos--; }
break;
case KEYDelete:
if (curpos < strlen(s)) deletechar(s, curpos);
break;
case KEYReturn: thisover = 1; break;
case KEYEsc: return;
default:
char c = ascii(k);
if (c && strlen(s) < num) {
insertchar(s, c, curpos++);
}
}
// 重繪行內容與游標位置
clearscrline(x, y, num);
putsxy(x, y, s);
gotoxy(x+curpos, y);
}
}
}
支援開啟、讀取、儲存、關閉檔案,使用順序表管理所有行:
void readdoc(FILE *fp) {
char s[20000], *newline;
struct line curline;
int linenum = 0;
while (!feof(fp)) {
getline(fp, s);
linenum++;
newline = (char *)malloc(strlen(s)+1);
strcpy(newline, s);
curline.base = newline;
curline.charnum = strlen(newline);
SequListInsert(Document, linenum, curline);
}
}
初始化選單系統、建立空文檔、進入事件迴圈處理鍵盤與選單命令:
int main() {
// 初始化選單、視窗、順序表
SequListInit(&Document, MAXLINES);
insertnewline(1); // 插入第一空行
while (!mainover) {
if (key = bioskey(1)) {
// 處理功能鍵:上下左右、Enter、Backspace、Ctrl+Y刪除行等
// 處理選單命令:開啟檔案、儲存、退出
}
}
closedoc(curfile);
}
此行編輯器展示了字串操作在實際應用中的核心邏輯:以順序表管理行集合,每行採用動態字串儲存,透過字元級的插入刪除演算法實現編輯功能。雖介面基於字元模式,但完整體現了文本編輯器的資料結構設計與操作原理,為理解現代編輯器架構奠定基礎。