Linux下如何用纯Pascal开发支持方向键交互的简易文本编辑器
Linux平台纯Pascal简易文本编辑器实现方案
现有基础与问题
当前已实现的代码支持新建文件、逐字符写入、按CTRL-Z退出写入流程,但存在两个核心缺陷:
- 仅支持新建覆盖写入,无法打开已有文件编辑
- 按键读取依赖行缓冲,没有处理扩展按键,无法通过方向键移动光标,不能在任意位置编辑
现有代码如下:
program textos; uses crt; var file_name: string; f: text; c: char; s: string; begin writeln('Enter the name of the file to create'); readln(file_name); writeln('Enter your text to be recorded in the file ', file_name); writeln('End with CTRL-Z followed by ENTER');writeln; assign(f, file_name); rewrite(f); repeat; read(c); write(f,c); until c=#26; {is equivalent to chr(26), which is CTRL-Z} close(f); writeln('Now look at the file ', file_name, ' in your directory'); writeln; writeln('Reading now from disk line by line and putting on screen:'); reset(f); {opening for reading} while (not eof(f)) do begin readln(f,s); writeln(s); end; end.
核心实现思路
1. 整体架构调整
放弃边输入边写磁盘的逻辑,所有编辑操作在内存缓冲区完成:
- 用动态字符串数组作为文本缓冲区,数组每个元素对应文本的一行
- 维护两个全局变量存储当前光标坐标:
cur_x记录列位置,cur_y记录行位置 - 程序启动时先让用户选择新建/打开文件:新建则初始化空缓冲区,打开则逐行读取文件内容存入缓冲区,之后把缓冲区内容全量绘制到屏幕,光标定位到(1,1)位置进入编辑循环
- 触发保存操作时,再把缓冲区全量写入磁盘文件
2. 方向键与扩展按键处理
已引入的crt单元已经提供了无回显、不需要回车确认的读键函数ReadKey,以及直接移动终端光标的函数GotoXY,不需要额外调用系统API或者第三方库。
方向键、功能键这类扩展按键的读取规则是:ReadKey第一次读到的字符是#0(空字符),此时需要再读一次字符,第二个字符的数值对应具体按键:
- 上方向键:
#72 - 下方向键:
#80 - 左方向键:
#75 - 右方向键:
#77
读到对应方向键时,修改cur_x/cur_y坐标即可,注意做边界校验: - 行坐标不能小于1,不能大于缓冲区总行数
- 列坐标不能小于1,不能大于当前行长度+1(行尾位置)
- 上下移动时如果当前列位置超过目标行的总长度,自动把光标对齐到目标行行尾
坐标修改完成后调用GotoXY(cur_x, cur_y)就能把终端光标移动到对应位置。
3. 编辑逻辑处理
- 普通可打印字符:读到后插入到当前光标所在的缓冲区位置,光标右移一位,同步在屏幕输出字符,重绘当前行光标后的内容避免显示错位
- 退格键(
#8):删除光标前一个字符,后续内容前移,同步更新屏幕 - 回车键(
#13):拆分当前行,把光标后的内容作为新行插入到缓冲区当前行的下位置,光标移动到新行行首 - 快捷键保留:可以继续用
CTRL-Z(#26)作为退出触发键,新增CTRL-S(#19)作为手动保存快捷键
核心编辑循环参考代码
var ch: char; cur_x, cur_y: integer; buffer: array of string; f: text; file_name: string; s: string; begin { 此处省略:新建/打开文件选择、文件内容读入buffer、屏幕初始绘制逻辑 } cur_x := 1; cur_y := 1; GotoXY(cur_x, cur_y); repeat ch := ReadKey; if ch = #0 then begin ch := ReadKey; case ch of #72: { 上方向键 } if cur_y > 1 then begin Dec(cur_y); if cur_x > Length(buffer[cur_y-1]) + 1 then cur_x := Length(buffer[cur_y-1]) + 1; GotoXY(cur_x, cur_y); end; #80: { 下方向键 } if cur_y < Length(buffer) then begin Inc(cur_y); if cur_x > Length(buffer[cur_y-1]) + 1 then cur_x := Length(buffer[cur_y-1]) + 1; GotoXY(cur_x, cur_y); end; #75: { 左方向键 } if cur_x > 1 then begin Dec(cur_x); GotoXY(cur_x, cur_y); end; #77: { 右方向键 } if cur_x <= Length(buffer[cur_y-1]) then begin Inc(cur_x); GotoXY(cur_x, cur_y); end; end; end else case ch of #26: Break; { CTRL-Z 退出编辑 } #19: { CTRL-S 手动保存 } begin Assign(f, file_name); Rewrite(f); for s in buffer do Writeln(f, s); Close(f); end; #8: { 退格键删除逻辑,补全边界处理即可 } begin if cur_x > 1 then begin Delete(buffer[cur_y-1], cur_x-1, 1); Dec(cur_x); { 此处补全屏幕重绘逻辑 } GotoXY(cur_x, cur_y); end; end; #13: { 回车键换行逻辑 } begin Insert('', buffer, cur_y); buffer[cur_y] := Copy(buffer[cur_y-1], cur_x, Length(buffer[cur_y-1]) - cur_x + 1); Delete(buffer[cur_y-1], cur_x, Length(buffer[cur_y-1]) - cur_x + 1); Inc(cur_y); cur_x := 1; ClrEol; Writeln; { 此处补全新行后续内容的屏幕重绘逻辑 } GotoXY(cur_x, cur_y); end; else { 普通可打印字符输入 } begin Insert(ch, buffer[cur_y-1], cur_x); Write(ch); Inc(cur_x); { 此处补全光标后内容的重绘逻辑 } GotoXY(cur_x, cur_y); end; end; until False; end.
注意事项
- Pascal中动态字符串数组下标默认从0开始,字符串下标从1开始,处理坐标和缓冲区对应关系时不要错位
- 所有修改缓冲区的操作都要同步更新屏幕显示,否则会出现内容和光标位置错位的问题
- 不需要额外依赖ncurses之类的第三方库,
crt单元提供的功能足够完成基础编辑器的所有交互需求
内容的提问来源于stack exchange,提问作者Puser
相关产品推荐
相关产品推荐

