2010-09-14 29 views
6

he implementado traducción de idiomas en una aplicación, poniendo todas las cadenas en tiempo de ejecución en un TStringList con:¿Cómo puedo buscar pares de nombre/valor más rápido en una Delphi TStringList?

procedure PopulateStringList; 
begin 
    EnglishStringList.Append('CAN_T_FIND_FILE=It is not possible to find the file'); 
    EnglishStringList.Append('DUMMY=Just a dummy record'); 
    // total of 2000 record appended in the same way 
    EnglishStringList.Sorted := True; // Updated comment: this is USELESS! 
end; 

entonces consigo la traducción usando:

function GetTranslation(ResStr:String):String; 
var 
    iIndex : Integer; 
begin 
    iIndex := -1; 
    iIndex := EnglishStringList.IndexOfName(ResStr); 
    if iIndex >= 0 then 
    Result := EnglishStringList.ValueFromIndex[iIndex] else 
    Result := ResStr + ' (Translation N/A)'; 
end; 

De todos modos, con este enfoque se tarda unos 30 microsegundos para ubicar un registro, ¿hay una mejor manera de lograr el mismo resultado?

ACTUALIZACIÓN: Para futuras referencias que escribo aquí la nueva aplicación que utiliza TDictionary como se sugiere (trabaja con Delphi 2009 y posteriores):

procedure PopulateStringList; 
begin 
    EnglishDictionary := TDictionary<String, String>.Create; 
    EnglishDictionary.Add('CAN_T_FIND_FILE','It is not possible to find the file'); 
    EnglishDictionary.Add('DUMMY','Just a dummy record'); 
    // total of 2000 record appended in the same way 
end; 


function GetTranslation(ResStr:String):String; 
var 
    ValueFound: Boolean; 
begin 
    ValueFound:= EnglishDictionary.TryGetValue(ResStr, Result); 
    if not ValueFound then Result := Result + '(Trans N/A)'; 
end; 

La nueva función GetTranslation realiza 1000 veces más rápido (en mi 2000 registros de muestra) luego la primera versión.

+1

Aunque los beneficios de 'IndexOf' de una TStringList se ordenan,' IndexOfName' no. Eso no quiere decir que * no * en alguna clase descendiente, pero TStringList no anula la búsqueda lineal básica que proporciona TStrings. –

+0

Sí, lo he comprobado, es lo mismo (así que puedo ahorrar tiempo sin ordenar la lista. Como no lo estoy modificando, tendría más sentido llamar a Sort, en lugar de configurarlo como True (de todos modos, no lo haré) .Gracias. – LaBracca

Respuesta

15

En Delphi 2009 o posterior usaría TDictionary < cuerda, cuerda> en Generics.Collections. También tenga en cuenta que hay herramientas gratuitas como http://dxgettext.po.dk/ para traducir aplicaciones.

+0

Gracias, sí, sé tdxggettext pero la implementación de la traducción que describí fue ya hecho por un desarrollador anterior, así que estoy tratando de mejorar eso, y con THasedStringList obtuve un resultado satisfactorio. De todos modos voy a probar (para mi conocimiento personal a la sugerencia TDictionary diste). – LaBracca

+1

@VilleK: Hay algunos errores en TDictionary y algunos de los otros genéricos en Delphi 2009, por lo que es mejor evitarlo a menos que use Delphi 2010 o XE. Y para ser sincero, hay muy poca documentación disponible sobre cómo usarlo y su funcionalidad. Me ha dado un tiempo mucho más difícil de lo que debería. – lkessler

+0

Sí, recuerdo que había un error con TDictionary en Delphi 2009, pero ahora lo uso mucho en Delphi 2010 y no he encontrado ningún problema. Creo que funciona genial También TObjectDictionary en la misma unidad es muy útil porque opcionalmente puede tomar posesión de claves y valores. Para mí, la unidad Generics.Collections es una de las razones principales para actualizar versiones anteriores de Delphi. –

17

THashedStringList debería ser mejor, creo.

+0

GRACIAS !!!! Ahora identificando 45 registros pasaron de 50 ms a 2 ms. 25 veces más rápido! – LaBracca

+1

Genial - de alguna manera nunca supe de esto. Tendré que investigarlo. –

12

Si THashedStringList funciona para usted, es genial. Su mayor debilidad es que cada vez que cambia el contenido de la lista, se reconstruye la tabla hash. Por lo tanto, funcionará para usted siempre que su lista sea pequeña o no cambie muy a menudo.

Para obtener más información al respecto, consulte: THashedStringList weakness, que ofrece algunas alternativas.

Si tiene una lista grande que puede actualizarse, puede intentar GpStringHash por gabr, que no tiene que recalcular toda la tabla en cada cambio.

+0

No necesito para actualizar la lista, se llena solo una vez, así que creo que puedo quedarme con THashedStringList porque ahora el cuello de botella se ha ido. – LaBracca

+0

+1 por 'THashedStringList debilidad' – SOUser

4

Creo que no utiliza la lista de cadenas de caracteres (lista de cadenas) correctamente. Esta es una lista ordenada, agrega elementos (cadenas), la ordena, pero cuando realiza una búsqueda, lo hace mediante una cadena parcial (solo el nombre, con IndexOfName).

Si utiliza IndexOfName en una lista ordenada, la TStringList no puede utilizar la búsqueda dicotómica. Utiliza la búsqueda secuencial.

(esto es la implementación de IndexOfName)

for Result := 0 to GetCount - 1 do 
    begin 
    S := Get(Result); 
    P := AnsiPos('=', S); 
    if (P <> 0) and (CompareStrings(Copy(S, 1, P - 1), Name) = 0) then Exit; 
    end; 

Creo que esta es la razón de los malos resultados.
La alternativa es usar 2 TStringList:
* La primera (ordenada) solo contiene el "Nombre" y un puntero a la segunda lista que contiene el valor; Puede implementar este puntero a la segunda lista usando el "puntero" de la propiedad Objeto.
* La segunda lista (no ordenada) contiene los valores.

Cuando busca, lo hace en la primera lista; En este caso, puede usar el método Find. cuando encuentre el nombre, el puntero (implementado con la propiedad Objeto) le otorgará la posición en la segunda lista con el valor.

En este caso, el método Find en Sorted List es más eficiente que HashList (que debe ejecutar una función para obtener la posición de un valor).

Atentamente.

Pd: Disculpe los errores con el inglés.

2

También puede utilizar una clase auxiliar para reprogramar la función "IndexOfName":

TYPE 
    TStringsHelper = CLASS HELPER FOR TStrings 
        FUNCTION IndexOfName(CONST Name : STRING) : INTEGER; 
        END; 

FUNCTION TStringsHelper.IndexOfName(CONST Name : STRING) : INTEGER; 
    VAR 
    SL : TStringList ABSOLUTE Self; 
    S,T : STRING; 
    I : INTEGER; 

    BEGIN 
    IF (Self IS TStringList) AND SL.Sorted THEN BEGIN 
     S:=Name+NameValueSeparator; 
     IF SL.Find(S,I) THEN 
     Result:=I 
     ELSE IF (I<0) OR (I>=Count) THEN 
     Result:=-1 
     ELSE BEGIN 
     T:=SL[I]; 
     IF CompareStrings(COPY(T,1,LENGTH(S)),S)=0 THEN Result:=I ELSE Result:=-1 
     END; 
     EXIT 
    END; 
    Result:=INHERITED IndexOfName(Name) 
    END; 

(o implementarlo en una clase TStrings descendientes si no le gusta ayudantes clase o no tenerlos en su Versión Delphi).

Esto utilizará una búsqueda binaria en una TStringList ordenada y una búsqueda secuencial en otras clases de TStrings.

Cuestiones relacionadas