文本编辑器中的行管理

时间:2011-12-03 22:28:29

标签: c++ winapi data-structures

我已经在文本编辑器上工作了一段时间。我从头开始制作了一个自定义编辑控件,现在我已经掌握了基础知识。我面临的问题是关于生产线管理。因为,我的程序依赖于将输入文本划分为行(文本逐行打印),所以行管理非常重要。我使用std :: vector来存储行位置。我正在使用Piece Table进行文本处理,但为了简单起见,我想说我有一个字符数组。每次用户按Enter键时,我都会在行向量中添加/插入一个元素。问题是每次用户插入一个字符时,整个结构都会受到干扰。例如:

         0   1   2   3   4   5    6   7   8   9   10
text = ['h','e','l','l','o','\n','W','o','r','l','d']
state of line vector : 
line[0] = 0 
line[1] = 6

假设用户在文本[2]之后插入一个字符('x'):

         0   1   2   3   4   5    6   7   8   9   10  11
text = ['h','e','l','x','l','o','\n','W','o','r','l','d'] 
state of line vector : 
line[0] = 0 
line[1] = 6

由于插入,我需要在当前行之后更新行向量中每个元素的值。删除相同。如果程序中有1000行并且用户编辑第一行,则更新所有999个元素(第一行除外)将非常低效。

我想到的是保持每条线彼此独立。但是,当现有生产线分为两条生产线时,这会导致复杂化。所以我想知道解决这个问题的好方法是什么?

编辑: 为了澄清,我使用的是名为Piece Table的数据结构。我没有使用一组字符。这是一个片表数据结构: http://www.cs.unm.edu/~crowley/papers/sds.pdf

3 个答案:

答案 0 :(得分:3)

许多编辑使用的经典数据结构是" Gap Buffer"。这基本上有一个工作空间,它围绕着活动发生的光标,以便本地操作快速发生。然后,当光标移动时,如果发生变化,间隙将随之移动。

就线计算而言,现代系统足够快,您可以非常简单地扫描缓冲区并查找线条。好处是你不需要在大多数操作中都这样做,所以你不要一直这样做。此外,缓冲区中的物理线(即,以EOL标记结束的字符集合)和软线(ala自动换行等)之间存在差异。考虑一个现代的文字处理器,其中段落通常是单个"线"但是换行到页边距。当然,您可以采用这种方式处理。

最后,对于键盘上的大多数操作,您可以简单地使用相对位置(即,如果您插入一个新行,那么它可以直接向行数组添加新的行标记,因为您已经知道了指出你在缓冲区内)。但是,当您执行多行的大型粘贴操作时,可能会更快地将其全部填入并重新计算整个缓冲区(作为替代方案,您可以始终将粘贴分解为行,并且在幕后一个接一个地插入它们,就像普通线一样。)

对于巨大的缓冲区或缓慢的慢速计算机,您可能需要考虑不要过多担心全局状态(确切地说缓冲区中有多少行,您可能正在使用哪条线等等)指出并开始对背景进行重新计算。很可能暂停会很轻微(但是如果你正在打字就会很烦人),并且只要人类停下来抓住他们的想法就会赶上。显然,这可能会使设计复杂化,并且您可能暂时在现代硬件上使用蛮力。

答案 1 :(得分:1)

Vector会正常工作。

考虑动态分配行,并让向量存储指向行的指针。将一堆指针移动到线上要比移动线本身便宜得多。

您可能还想考虑某种Gap Buffer techniques.

答案 2 :(得分:0)

如果我理解了这个问题,你就会沿着这些线跟踪具有辅助数据结构的线的位置:

line  offset  length
   0       0      65
   1      65      30
   2      95      50
   3     145       1
   4     146      13
 ...

如果行n的长度改变d,则必须用d更新所有剩余行的偏移量。当有很多行时,这很慢。

你可以跟踪地标。不是从序列的开始处偏移,而是让它们相对于某个地标。

假设您为每100行创建一个地标。由于第一个地标位于文件的开头,因此前100行被跟踪相同。但接下来的一百行只有偏移量,而地标与行100的文件开头有绝对偏移量。

因此,当您更改线条的长度时,您只需要更新该地标中其余线条的偏移量,以及其余地标的偏移量。那仍然是O(n),但是有一个相当大的除数会使它更快。

但我们可以做得更好。我们不是仅仅维护一个地标列表,而是假设我们将它们放在一棵树中,树的叶子是你的线,而root表示整个文件。要查找给定行的偏移量,请将其所有祖先的偏移量添加到一起。如果一行改变,你只需更新一个节点及其祖先。这给出了O(log n),代价是一些簿记。空间开销并不比你已经使用的双向链表差得多。