基于C++和C#实现的文本编辑器

Gappsong

发布日期: 2019-04-03 10:29:49 浏览量: 317
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

一、程序框架

  • 图形交互:负责显示。给用户提供图形界面,接受并处理用户的输入,将输入转换为文本编辑器核心所接受的形式,对数据进行处理

  • 文本编辑器核心:负责对数据进行操作。接收图形界面发送的命令,对文本进行增删查改,写入读取等操作,并将内容输出到图形界面交互所提供的输出缓冲区中,交予其进行输出

  • 文件交互:将文本内容写入外存,或从中读取文本内容

  • 模式匹配:对给定的模式串在文本内容中进行查找,并将查找结果返回图形交互界面进行输出

二、数据结构

  1. class CEditorCore 主要成员变量及成员函数
  2. {
  3. private:
  4. list<string> data; // 链表形式存储文本内容,每一行为链表的每一个元素
  5. string clipboard; // 内置剪切板,用于在拖放操作时存储临时数据
  6. string fileName; // 打开文件时的文件名
  7. public:
  8. string* outputBuffer; // 输出缓冲区指针,由图形交互界面提供其地址
  9. bool loadFromFile(string); // 从指定文件读取文本
  10. bool saveToFile(string); // 写入到指定文件
  11. bool insert(const CPosition &, const string &); // 插入一个字符串
  12. string remove(const CPosition &, const CPosition &); // 删除段落,指定开始结束为止
  13. string getSelection(const CPosition &, const CPosition &) const; // 获得选中的字符串
  14. bool replace(const string &, const CPosition &, const CPosition &); // 替换指定位置的字符串
  15. int updateBuffer(); // 更新输出缓冲区,返回行最大宽度
  16. };

三、主要算法

3.1 KMP算法

模板串为W,待匹配串为T。

首先要对W串进行预处理,即求出next数组(最大前缀长度)具体实现可以用递归实现,代码如下:

  1. while ( T[j+1] != '\0' )
  2. {
  3. if (k == -1 || T[j] == T[k])
  4. {
  5. ++j; ++k;
  6. if (T[j]!=T[k])
  7. next[j] = k;
  8. else
  9. next[j] = next[k];
  10. }// if
  11. else
  12. k = next[k];
  13. }// while

之后就是具体的模式匹配过程。

假设在模式匹配的进程中,执行T[i]和W[j]的匹配检查。若T[i]=W[j],则继续检查T[i+1]和W[j+1]是否匹配。若T[i]<>W[j],则分成两种情况:若j=1,则模式串右移一位,检查T[i+1]和W[1]是否匹配;若1<j<=m,则模式串右移j-next(j)位,检查T[i]和W[next(j)]是否匹配。重复此过程直到j=m或i=n结束。

代码如下:

  1. int index=0,i=0,j=0;
  2. while(Text[i]!='\0' && Pattern[j]!='\0' )
  3. {
  4. if(Text[i]== Pattern[j])
  5. {
  6. ++i;// 继续比较后继字符
  7. ++j;
  8. }
  9. else
  10. {
  11. index += j-next[j];
  12. if(next[j]!=-1)
  13. j=next[j];// 模式串向右移动
  14. else
  15. {
  16. j=0;
  17. ++i;
  18. }
  19. }
  20. }//while
  21. if(Pattern[j]=='\0')
  22. return index;

3.2 BM算法

模板串为p,长度lp;待匹配串为t,长度lt。

则算法可表示为:

  1. while (i <= lt - lp)
  2. {
  3. j = lp - 1;
  4. while (j > -1 && t[i + j] == p[j]) // 从后向前匹配,直到找到匹配失败的字符
  5. --j;
  6. if (j == -1)
  7. break; // 匹配成功
  8. else
  9. i += shift; // 移动模板串的位置
  10. }

从后向前匹配,利用两个并行算法——坏字符与好后缀,增大每次匹配的移位量(shift),达到减少匹配次数以加快查找速度的目的。

坏字符:定义辅助数组bc[],预处理时记录每个字符在最右边出现的位置,若在匹配过程中发现某个字符不相等,直接将模板串中最右侧的相应字符与其对齐:

  1. Shiftbc = j - bc[t[i + j]];

好后缀:定义辅助数组gs[],预处理时记录模板串中与某后缀完全相同子串的位置,若在匹配过程中匹配失败,则直接将模板串移动到此位置:

  1. Shiftgs = j + 1 - gs[j + 1];

最后实际的位移:

  1. i+=max(shiftbc, shiftgc)

四、实现功能

  • 文件的新建,打开,保存

  • 图形界面与鼠标、键盘操作

  • 完全从底层编写的文本框、光标及其动画效果

  • 在任意位置添加、删除字符串,剪贴板操作

  • 字符串查找与替换(KMP算法、BM算法),全部替换

  • 命令行/鼠标拖放打开文件,选中文本段拖放移动/复制

  • 更改字体颜色、字号、行高等

  • 显示行号,括号匹配

  • 提供了一个方便的表达式计算工具QuickParse™

五、调试报告

调试过程是艰辛而枯燥的……由于前期一直没有结合所有模块,因而在最后结合时发现了大量的问题,特别是C#与C++之间的跨语言类型转换包装之类的奇怪问题,以及前台显示与后台的核心之间的交流。

最初的问题是对于整个文本内容,在用P/Invoke调用Dll时,C#动态数组、动态字符串与C++链表、字符串的对应问题,后来的解决方式是通过先调用C++模块获得每行宽度,在C#中分配空间,而后调用更新缓冲区函数,利用Marshal将C#中的数组指针传入并在函数中放入整个文本。后来的问题是MDA(托管调试助手)经常抛出致命异常,即程序内存被破坏,后来通过给数组分配更大的空间、改变调用逻辑来解决。这些调错经历让我深刻明白了跨语言编程的困难,在以后的编程中,除非接口很简单,否则不要轻易跨语言编程。

之后,在核心模块中遇到了大量的细节问题,比如误把string的最后一位当作-1、if表达式==写成=、循环中的迭代器变量没有自增等等。

另外,后期在支持通配符的KMP算法中发现bug,因而暂时用BM算法代替,修复bug后增加了算法选择功能,也算是因祸得福吧……

六、程序截图

上传的附件 cloud_download 基于C++和C#实现的文本编辑器.7z ( 196.99kb, 1次下载 )
error_outline 下载需要9点积分

发送私信

风很清澈,从头到脚都快乐

10
文章数
9
评论数
最近文章
eject