Question

i ont une fonction qui est la tâche est de convertir un ADO Recordset en html:

class function RecordsetToHtml(const rs: _Recordset): WideString;

Et le courage de la fonction implique beaucoup de concaténation grande chaîne:

   while not rs.EOF do
   begin
      Result := Result+CRLF+
         '<TR>';

      for i := 0 to rs.Fields.Count-1 do
         Result := Result+'<TD>'+VarAsWideString(rs.Fields[i].Value)+'</TD>';

      Result := Result+'</TR>';
      rs.MoveNext;
    end;

Avec quelques milliers de résultats, la fonction prend, ce que tout utilisateur se sentirait, est trop long à courir. Delphi échantillonnage Générateur de profils montre que les 99,3% du temps est consacré à WideString concaténation (@WStrCatN et @WstrCat).

Quelqu'un peut-il penser à un moyen d'améliorer concaténation WideString? Je ne pense pas que Delphi 5 a une sorte de constructeur de chaîne. Et Format ne prend pas en charge Unicode.


Et pour que personne ne tente de belette: prétendez-vous implémentez l'interface:

IRecordsetToHtml = interface(IUnknown)
    function RecordsetToHtml(const rs: _Recordset): WideString;
end;

Mise à jour un

Je pensais à l'aide d'un IXMLDOMDocument, pour construire le code HTML en XML. Mais alors je compris que le code HTML final serait xhtml et non html - une différence subtile, mais importante,

.

Mise à jour Deux

article de base de connaissances Microsoft: Comment améliorer Concaténation performance

Était-ce utile?

La solution 4

je l'ai trouvé la meilleure solution. La open source HTMLParser pour Delphi, a une classe TStringBuilder d'aide. Il est utilisé en interne pour construire ce qu'il appelle DomStrings, qui est en fait un alias de WideString:

TDomString = WideString;

Avec un peu de tripotage de sa classe:

TStringBuilder = class
public
   constructor Create(ACapacity: Integer);
   function EndWithWhiteSpace: Boolean;
   function TailMatch(const Tail: WideString): Boolean;
   function ToString: WideString;
   procedure AppendText(const TextStr: WideString);
   procedure Append(const value: WideString);
   procedure AppendLine(const value: WideString);
   property Length: Integer read FLength;
end;

Les entrailles de la routine devient:

while not rs.EOF do
begin
   sb.Append('<TR>');

   for i := 0 to rs.Fields.Count-1 do
      sb.Append('<TD>'+VarAsWideString(rs.Fields[i].Value));

   sb.AppendLine('</TR>');

   rs.MoveNext;
end;

Le code puis se sent pour exécuter infiniment afaster. spectacles de profilage beaucoup amélioration; la manipulation de WideString et de comptage de longueur deviennent négligeables. Dans sa place était opérations internes FastMM.

Remarques

  1. Belle prise sur le forçant erreur de toutes les chaînes dans le code actuel page (VarAsString plutôt que VarAsWideString)
  2. Quelques balises de fermeture HTML sont facultatifs; omis ceux qui font logiquement aucun sens.

Autres conseils

WideString sont par nature lente, car ils ont été mis en œuvre pour assurer la compatibilité COM et passer par des appels COM. Si vous regardez le code, il continuera à réaffectant la chaîne et appelez SysAllocStringLen () et C qui sont des API de oleaut32.dll. Il n'utilise le gestionnaire de mémoire Delphi, mais il utilise le AFAIK gestionnaire de mémoire COM. Parce que la plupart des pages HTML ne pas utiliser UTF-16, vous pouvez obtenir un meilleur résultat en utilisant le type de chaîne Delphi natif et une liste de chaînes, bien que vous devriez faire attention à la conversion de UTF et la codepage réelle, et la conversion déclasse les performances et . De plus vous utilisez une fonction VarAsString () qui convertit probablement une variante à une AnsiString , puis converti en un WideString. Vérifiez si votre version de Delphi a un VarAsWideString () ou quelque chose fonction aussi bien pour l'éviter, ou se fonder sur la conversion automatique Delphi si vous pouvez être sûr que votre variante ne sera jamais NULL.

Eh oui, votre algorithme est clairement O (n ^ 2).

Au lieu de retourner un string, essayer de retourner un TStringList et remplacer votre boucle avec

   while not rs.EOF do
   begin
      Result.Add('<TR>');

      for i := 0 to rs.Fields.Count-1 do
         Result.Add( '<TD>'+VarAsString(rs.Fields[i].Value)+'</TD>' );

      Result := Result.Add('</TR>');
      rs.MoveNext;
    end;

Vous pouvez ensuite enregistrer votre Result en utilisant TStringList.SaveToFile

Je suis incapable de passer le temps en ce moment pour vous donner le code exact.

Mais je pense que vous pouvez faire la chose la plus rapide est:

  1. boucle par toutes les chaînes et leur longueur totale ajoutant aussi pour les balises de table supplémentaires dont vous aurez besoin.

  2. Utilisation SetString d'allouer une chaîne de la bonne longueur.

  3. boucle par toutes les chaînes à nouveau et utiliser le "Move" procédure pour copier la chaîne à la place dans la chaîne finale.

L'essentiel est que beaucoup de concaténations à une chaîne prennent plus de temps et plus en raison de la constante et allouant libération de la mémoire. Une allocation unique sera votre plus grand gain de temps.

WideString est fait référence ne compte pas, toute modification des moyens de manipulation de chaîne. Si votre contenu n'est pas UNICODE codé , vous pouvez utiliser la chaîne interne native (comptée de référence) à concaténer chaîne, puis le convertir en un WideString. L'exemple est le suivant:

var
  NativeString: string;
begin
   // ...
   NativeString := '';

   while not rs.EOF do
   begin
     NativeString := NativeString + CRLF + '<TR>';

     for i := 0 to rs.Fields.Count-1 do
       NativeString := NativeString + '<TD>'+VarAsString(rs.Fields[i].Value) + '</TD>';

     NativeString := NativeString + '</TR>';
     rs.MoveNext;
   end;

   Result := WideString(NativeString);

Je l'ai vu aussi une autre approche: Encode Unicode UTF8String (comme référence compté), et enfin les concaténer convertir UTF8String à WideString. Mais je ne suis pas sûr, si deux UTF8String peut concaténer directement. Le temps sur le codage doit également être pris en considération.

Quoi qu'il en soit, bien que concaténation WideString est beaucoup plus lent que les opérations de chaîne d'origine. Mais il est encore acceptable OMI. réglage trop sur ce genre de chose doit être évitée. envisage sérieusement de la performance, vous devez ensuite mettre à niveau votre Delphi au moins 2009. Les coûts sur l'achat d'un outil est pour moins cher à long terme que de faire hacks lourds sur une vieux Delphi.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top