首页 文章

文本编辑器如何实现?

提问于
浏览
48

这个问题可能会让我听起来很无能为力 . 那是因为我 .

我只是想,如果我假设有兴趣设计我自己的文本编辑器GUI控件,小部件或任何你想要调用的东西(我不是),我怎么会这样做呢?

像我这样的新手的诱惑是以字符串的形式存储文本编辑器的内容,这看起来非常昂贵(并不是说我太熟悉字符串实现在一种语言/平台和下一种语言/平台之间的区别;但是我知道在.NET中,例如,它们是不可变的,所以频繁的操作,例如你需要在文本编辑器中支持的内容,将会非常浪费,在一个接一个地连续构建一个字符串实例) .

据推测,使用包含文本的一些可变数据结构;但弄清楚这个结构可能会是什么样的,这对我来说是一个挑战 . 随机访问会很好(我想,无论如何 - 毕竟,不要像在用户输入的每个字符一样,会有大量数据到"shift" .

因此,如果我不得不猜测,我认为文本编辑器采用某种结构将文本分成更小的部分(行,可能?),它们各自包含随机访问的字符数组,并且它们本身是随机的可作为离散块访问 . 即使这看起来似乎必须是一个相当可怕的过度简化,但是,如果它甚至远远接近开始 .

当然我也意识到可能没有文本编辑器实现的方式;也许它在一个编辑器之间变化很大 . 但我认为,由于它被多次,多次地解决,这些年来可能已经出现了一种相对普遍的方法 .

无论如何,我只是想知道是否有人对这个主题有一些了解 . 就像我说的那样,我绝对不想写自己的文本编辑器;我只是好奇 .

4 回答

  • 2

    一种常见的技术(特别是在较旧的编辑器中)称为分割缓冲区 . 基本上,您将文本“分解”为光标之前的所有内容以及光标之后的所有内容 . 之前的一切都在缓冲区的开头 . 之后的所有内容都在缓冲区的末尾 .

    当用户键入文本时,它会进入中间的空白区域而不移动任何数据 . 当用户移动光标时,您可以将相应数量的文本从“中断”的一侧移动到另一侧 . 通常情况下,围绕一个区域移动很多,所以通常一次只能移动少量文本 . 最大的例外是如果你有一种“转向xxx”的能力 .

    查尔斯克劳利写了一个更完整的discussion of the topic . 您可能还想查看The Craft of Text Editing,它更深入地介绍了分割缓冲区(以及其他可能性) .

  • 36

    前段时间,我在Tcl中编写了自己的文本编辑器(实际上,我从某个地方窃取了代码并将其扩展到无法识别,开源的奇迹啊) .

    正如您所提到的,对非常非常大的字符串执行字符串操作可能很昂贵 . 因此编辑器会在每个换行符(“\ n”或“\ r \ n”或“\ r \ n”或“\ r \ n”)中将文本拆分为较小的字符串 . 所以我剩下的就是在行级编辑小字符串并在行之间移动时进行列表操作 .

    这样做的另一个好处是,它是一个简单而自然的概念 . 我的思想已经认为,每一行文本都要通过多年的编程来加强,其中新行具有风格或语法上的重要性 .

    它也有助于我的文本编辑器的用例是程序员编辑器 . 例如,我实现了语法高亮,但没有实现单词/换行 . 所以在我的情况下,文本中的换行符和屏幕上绘制的行之间有一个1:1的映射 .

    如果你想看看,这是我的编辑器的源代码:http://wiki.tcl.tk/16056

    这不是玩具BTW . 我每天都使用它作为我的标准控制台文本编辑器,除非文件太大而不适合RAM(严重的是,什么文本文件是什么?甚至小说,通常是4到5 MB,适合RAM . 我只看到日志文件增长到数百MB) .

  • 1

    根据一次需要在编辑器中的文本量,整个缓冲区方法的一个字符串可能没问题 . 我认为记事本这样做 - 注意到在大文件中插入文本会有多慢?

    散列中每行一个字符串表似乎是一个很好的妥协 . 它可以导航到特定的行并删除/粘贴效率,而不会有太多复杂性 .

    如果你想实现一个撤销功能,你需要一个表示,它允许你回到以前的版本而不存储整个文件的30个副本进行30次更改,尽管如果文件足够小,那么这也许会很好 .

  • 1

    最简单的方法是使用该语言提供的某种字符串缓冲类 . 即使是简单的char对象数组也会在紧要关头完成 .

    然后,添加,替换和搜索文本相对较快 . 当然,其他操作可能更耗时,因为在缓冲区的开头插入一系列字符是更昂贵的操作之一 .

    但是,对于简单的用例,这在性能方面可能完全可以接受 .

    如果插入和删除的成本特别重要,我很想通过创建一个内部维护缓冲区对象列表的缓冲区包装类来进行优化 . 未在现有缓冲区尾部发生的任何操作(除了简单替换)将导致相关缓冲区在相关点处被分割,因此缓冲区可以在其尾部进行修改 . 但是,外包装器将保持与简单缓冲区相同的接口,因此我不必重写例如我的搜索行动 .

    当然,这种简单的方法很快会以极其分散的缓冲区结束,并且我会考虑在适当的时候使用某种规则来合并缓冲区,或者在例如适当的情况下推迟分割缓冲区 . 单个字符插入 . 也许规则是我最多只有2个内部缓冲区,并且在创建新缓冲区之前我会合并它们 - 或者当有人要求我立刻查看整个缓冲区时 . 不确定 .

    重点是,我开始简单但通过精心选择的界面访问可变缓冲区,如果分析显示我需要,则使用内部实现 .

    但是,我绝对不会从不可变的String对象开始!

相关问题