2010-11-16 14 views
33

Esta es una pregunta de la entrevista. ¿Qué estructura de datos usarías para almacenar el texto en un editor de texto?Estructura de datos para el editor de texto

+1

Por favor, véase: [teoría editor de texto] (http: // stackoverflow.com/questions/3169440/text-editor-theory/3169491) –

Respuesta

31

En el viejo ZX-Spectrum uno (o más, no sé) text exditor usó una estructura muy simple.

Había un gran búfer, que ocupaba toda la memoria RAM libre. El texto se dividió en dos partes con el cursor. Parte anterior al cursor, se colocó al principio del búfer y el resto al final del búfer. A medida que se escribe el texto, los datos simplemente se agregan al final de la primera parte, y cuando se mueve el cursor, el texto se copia y retrocede. diseño

Buffer:

Hello, World! 
     ^------Cursor here 

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
|H|e|l|l|o|,| |W| <free> |o|r|l|d|!| 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
|    ^  ^  | 
begin   cur1  cur2 end 

eso es, cómo se hizo algunas operaciones de edición:

Type a char: buffer[cur1++] = character 

Backspace:  cur1-- 

Cursor left: buffer[--cur2] = buffer[--cur1] 

Cursor right: buffer[cur1++] = buffer[cur2++] 

búfer en acción:

   Hello, W..............orld! 
Press right  ^   ^
      Hello, Wo..............rld! 
Press backspace  ^   ^
      Hello, W...............rld! 
Press 0   ^   ^
      Hello, W0..............rld! 
        ^   ^
+7

Para referencia: Esto se llama "buffer gab". La mayoría de las implementaciones no mueven el búfer cuando mueves el cursor. Simplemente lo hacen en operaciones de inserción/eliminación. –

+0

@ Aaron Digulla: Gracias, buena adición. Ambas implementaciones tienen sus razones. – Vovanium

+14

Hay un error tipográfico allí: se llama ** gap buffer ** Y aquí está [más información de Wikipedia] (http://en.wikipedia.org/wiki/Gap_buffer) –

29

Rope

Una cuerda es esencialmente un árbol binario cuyas hojas son matrices de caracteres. Un nodo en el árbol tiene un hijo izquierdo y un hijo derecho: el niño izquierdo es la primera parte de la cadena, mientras que el niño derecho es la parte final de la cadena. La concatenación de dos cables simplemente implica la creación de un nuevo nodo de árbol con ambas cuerdas como elementos secundarios. Para garantizar la indexación del tiempo logarítmico y las operaciones de subcadena, es posible que la cuerda resultante deba equilibrarse. Varias estrategias de equilibrio son posibles.

Las ventajas principales de los cables en comparación con el almacenamiento de cadenas como matrices de caracteres es que permiten una concatenación mucho más rápida que las cadenas ordinarias, y no requieren un gran espacio contiguo de memoria para almacenar una cadena grande. Las principales desventajas son un mayor uso general del espacio y una indexación más lenta, que se vuelven más graves a medida que la estructura del árbol se hace más grande y más profunda. Sin embargo, muchas aplicaciones prácticas de indexación implican solo una iteración sobre la cadena, que permanece rápida siempre que los nodos de hoja sean lo suficientemente grandes como para beneficiarse de los efectos de caché.

+1

+1 por decirme que la estructura que reinventé (sic), describí y sugerí en una de mis publicaciones se llama oficialmente :-) – thkala

+0

@thkala: paga investigar primero, nada nuevo bajo el sol ;-) –

+0

Además de la concatenación, las cuerdas suelen ser relativamente buenas (en comparación con el almacenamiento completamente contiguo) para muchas acciones de eliminación, inserción y sustitución, especialmente cerca del inicio del documento o donde hacer crecer el almacenamiento contiguo requeriría un movimiento en la memoria. –

6

Sé que es demasiado tarde para una respuesta, pero encontré el libro The Craft of Text Editing realmente útil. Contiene la descripción de varios modelos de búfer con sus pros y sus contras. Lamentablemente, no menciona la estructura de datos de Ropes.

1

Como @Vovanium ya mencionó la teoría básica de cómo se puede utilizar el búfer de huecos, he implementado una versión de C/C++.

Código:

#include <stdio.h> 
#define SZ 1000 

char leftArray[SZ], rightArray[SZ]; 
int leftCount, rightCount; 
int cursorPos; 

/* 
* Private APIs 
*/ 

void printArray(){ 

    for(register int i = 0; i < leftCount; i++){ 
     printf("%c", leftArray[i]); 
    } 

    for(register int i = rightCount - 1; i >= 0; i--){ 
     printf("%c", rightArray[i]); 
    } 
    printf("\n"); 
} 

void adjust(int pos){ 

    while(leftCount > pos){ 
     rightArray[rightCount++] = leftArray[--leftCount]; 
    } 

    while(leftCount < pos){ 
     leftArray[leftCount++] = rightArray[--rightCount]; 
    } 
} 


/* 
* Public APIs for Text Editor 
*/ 

void init(){ 

    cursorPos = leftCount = rightCount = 0; 
} 

void addWord(char word[], int len){ 

    adjust(cursorPos); 

    for(register int i = 0; i < len; i++){ 
     leftArray[leftCount++] = word[i]; 
    } 
    leftArray[leftCount] = 0; 
} 

void addBackSpace(){ 

    adjust(cursorPos); 
    leftCount--; 
} 

void moveCurson(int newPosition){ 

    cursorPos = newPosition; 
} 

void subString(int pos, int length, char result[]){ 

     adjust(cursorPos); 

    int k = 0; 
     int l = 0; 
    while(k + pos < leftCount && k < length){ 
     result[k] = leftArray[pos + k]; 
     k++; 
    } 

    length -= k; 
    while(l < length){ 
     result[k++] = rightArray[rightCount - 1 - l]; 
     l++; 
    } 
} 
Cuestiones relacionadas