2015-08-11 2 views
4

Je dois diviser une chaîne en une TStringList avec des sous-chaînes de longueur fixe.Une méthode rapide pour diviser une chaîne en parties de longueur fixe dans Delphi

Actuellement j'utilise:

procedure StrToStringList(ASource: string; AList: TStrings; AFixedLen: Integer); 
begin 
    Assert(Assigned(AList)); 
    while Length(ASource) > AFixedLen do 
    begin 
     AList.Add(LeftStr(ASource, AFixedLen)); 
     Delete(ASource, 1, AFixedLen); 
    end; 
    AList.Add(ASource); 
end; 

Cela fonctionne, mais semble être lent. Une idée meilleure/plus rapide?

Modifié: Profilage des résultats:

Le gain de vitesse est assez impressionnant. Voici les résultats de mon profilage (subjectif).

Taille des données: 290KB, FixedLen: 100:

  • Code d'origine: 58 ms
  • Heffernan: 1 ms
  • Deltics: 1 ms

Taille des données: 2805KB, FixedLen : 100:

  • Code d'origine: 5803 ms
  • Heffernan: 5 ms
  • Deltics: 4 ms
+3

Évidemment, l'appel 'Delete' n'est pas nécessaire. –

+0

C'est Rud-y, pas Rud-i. Je parie que vous pourriez voir cette différence? –

Répondre

5

Il y a quelques optimisations immédiatement évidentes possibles dans votre code:

  1. Ne pas modifier la chaîne source, il suffit d'extraire les nécessaires sous-chaînes. Vous pouvez ensuite le paramètre chaîne d'entrée const, permettant au compilateur d'optimiser encore les appels effectués à la procédure

  2. Puisque vous avez affaire à des longueurs fixes et une chaîne d'entrée de longueur connue, vous pouvez pré-calculer la capacité requise de la liste de chaînes et d'éviter les réaffectations de mémoire que la liste est ajoutée à.

Voici comment j'aller à ce sujet:

procedure StrToStringList(const aSource: String; 
          const aList: TStrings; 
          const aFixedLen: Integer); 
var 
    idx: Integer; 
    srcLen: Integer; 
begin 
    aList.Capacity := (Length(aSource) div aFixedLen) + 1; 

    idx := 1; 
    srcLen := Length(aSource); 

    while idx <= srcLen do 
    begin 
    aList.Add(Copy(aSource, idx, aFixedLen)); 
    Inc(idx, aFixedLen); 
    end; 
end; 

Le calcul de la capacité ici peut entraîner une capacité excédentaire de 1 où la chaîne d'entrée divise exactement par la longueur fixe, mais est des frais généraux négligeables et acceptables lorsque l'objectif est une performance optimale (l'alternative est une branche conditionnelle pour modifier ou calculer la capacité différemment pour répondre à ce qui est susceptible d'être la minorité des cas).

+0

Accepté, ce code semble être légèrement plus rapide que l'autre. –

7

Je pense qu'il est inutile de modifier la chaîne d'entrée. Évitez cela comme ceci:

var 
    Remaining: Integer; 
    StartIndex: Integer; 
begin 
    Remaining := Length(ASource); 
    StartIndex := 1; 
    while Remaining > 0 do 
    begin 
    AList.Add(Copy(ASource, StartIndex, AFixedLen)); 
    inc(StartIndex, AFixedLen); 
    dec(Remaining, AFixedLen); 
    end; 
end; 

Cela permettra de réduire le montant de l'allocation de tas, et la copie.

Cependant, je ne serais pas surpris si vous avez observé peu de gain de performance. Afin d'effectuer une optimisation sérieuse, nous aurions probablement besoin de voir des exemples de données d'entrée. Ne pas utiliser delete à l'intérieur de la boucle.

+0

Oui, votre solution est plus rapide. Merci. Il s'agit de prétraiter différents blocs de données COBOL qui doivent être lus. Une optimisation sérieuse éviterait certainement de diviser les données en StringLists mais pour le moment je cherche des façons d'optimiser sans réécrire la structure sous-jacente. –

+0

Si les éléments sont de longueur fixe, les listes de chaînes peuvent ne pas être la meilleure approche. Les tableaux de caractères seraient probablement plus rapides. Je suis sûr que vous pouvez faire beaucoup mieux que le code ici, mais je ne pense pas que nous ayons suffisamment de détails pour être plus précis. –

+0

Après un certain profilage, je suis impressionné par la rapidité de cette approche basée sur TStringList. Les données COBOL sont divisées en ensembles de données par la fonction ici. Ensuite, le jeu de données par jeu de données est analysé à partir de la liste de chaînes. Pas besoin d'optimisation supplémentaire sur ce front pour le moment. –

2

Cela provoque le déplacement de la chaîne entière. Utilisez une variable d'index integer commençant à 1 et augmentez-la chaque fois par AFixedLen après avoir utilisé copy pour extraire la sous-chaîne jusqu'à la fin.