Domanda

Ho una funzione che è compito è quello di convertire un ADO Recordset in HTML:

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

E il coraggio della funzione comporta un sacco di un'ampia concatenazione di stringhe:

   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;

Con poche migliaia di risultati, la funzione prende, quello che ogni utente si sentirebbe, è troppo lungo per l'esecuzione. Il Delphi campionamento Profiler spettacoli che 99.3% del tempo è trascorso in WideString concatenazione (@WStrCatN e @WstrCat).

Qualcuno può pensare a un modo per migliorare la concatenazione WideString? Non credo Delphi 5 ha alcun tipo di stringa builder. E Format non supporta Unicode.


E per assicurarsi che nessuno cerca di donnola fuori: fingere si implementa l'interfaccia:

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

Aggiornamento One

ho pensato di utilizzare un IXMLDOMDocument, per costruire il codice HTML in formato XML. Ma poi mi sono reso conto che il codice HTML finale sarebbe xhtml e non html - una sottile, ma importante, differenza

.

Aggiornamento Due

articolo della Microsoft Knowledge Base: Come migliorare concatenazione di stringhe prestazioni

È stato utile?

Soluzione 4

ho trovato la soluzione migliore. L'open source HTMLParser per Delphi, ha una classe di supporto TStringBuilder. Viene utilizzato internamente per costruire ciò che egli chiama DomStrings, che è in realtà un alias di WideString:

TDomString = WideString;

Con un po 'di armeggiare della sua 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;

Le viscere della routine diventa:

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;

Il codice quindi si sente per eseguire infinitamente afaster. spettacoli profilatura molto di miglioramento; la manipolazione e la lunghezza WideString conteggio diventato trascurabile. Al suo posto c'era proprie operazioni interne di FastMM.

Note

  1. Bella presa sulla erronea forzatura di tutte le stringhe in corrente code-page (VarAsString piuttosto che VarAsWideString)
  2. Alcuni tag di chiusura HTML sono opzionali; quelli omessi che logicamente non hanno senso.

Altri suggerimenti

WideString sono intrinsecamente lenti perché sono stati implementati per COM compatibilità e passare attraverso chiamate COM. Se si guarda il codice, si continuerà a riallocazione stringa e chiamare SysAllocStringLen () & C, che sono le API da oleaut32.dll. Non usa il gestore della memoria Delphi, ma per quanto ne so utilizza il gestore di memoria COM. Poiché la maggior parte delle pagine HTML non usano UTF-16, è possibile ottenere un risultato migliore utilizzando il tipo stringa Delphi nativa e una lista di stringhe, anche se si dovrebbe essere attenti a conversione da UTF e la tabella codici attuale, e la conversione sarà influire sulle prestazioni pure . Inoltre si sta utilizzando una funzione VarAsString () che, probabilmente, converte una variante a un AnsiString , quindi convertito in una WideString. Verificare se la versione di Delphi ha una VarAsWideString () o qualcosa di simile funzione per evitarlo, o affidarsi a Delphi conversione automatica se si potesse essere sicuro che la variante non sarà mai NULL.

Yup, l'algoritmo è chiaramente in O (n ^ 2).

Invece di restituire un string, provare a restituire un TStringList, e sostituire il vostro ciclo con

   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;

È quindi possibile salvare il Result utilizzando TStringList.SaveToFile

Sono in grado di passare il tempo in questo momento per darvi il codice esatto.

Ma penso che la cosa più veloce che puoi fare è:

  1. ciclo tra tutti gli archi e la loro lunghezza totale anche l'aggiunta dei tag table in più di cui ha bisogno.

  2. Usa SetString per assegnare una stringa di lunghezza appropriata.

  3. Loop attraverso tutte le corde di nuovo e l'uso il "Move" procedura per copiare la stringa al posto giusto nella stringa finale.

La cosa fondamentale è che molti concatenazioni a un introito stringa più lunga e più a lungo a causa della costante allocazione e la liberazione della memoria. Una dotazione unica sarà il tuo più grande risparmio di tempo.

WideString non è il riferimento contato, qualsiasi modifica comporta una manipolazione stringa. Se il contenuto non è Unicode codificato , è possibile utilizzare internamente la stringa originaria (di riferimento contati) a stringa concatena e poi convertirlo in un WideString. Esempio è la seguente:

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);

Ho visto anche un altro approccio: Codifica Unicode per UTF8String (come riferimento contato), li concatena e infine convertire UTF8String a WideString. Ma io non sono sicuro, se due UTF8String può essere concatenato direttamente. Il tempo sulla codifica deve essere considerato.

In ogni caso, anche se WideString concatenazione è molto più lento di operazioni sulle stringhe native. Ma è IMO ancora accettabile. Troppo messa a punto su tale genere di cose dovrebbe essere evitato. Scherzi a considerare di prestazioni, si dovrebbe quindi aggiornare il tuo Delphi ad almeno il 2009. I costi per l'acquisto di uno strumento è a lungo termine più conveniente che fare hack pesanti su un vecchio Delphi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top