Procesarea rapidă a datelor excelează în produsele software Delphi

Pentru un an și jumătate mi sa oferit o grămadă de opțiuni cu privire la modul de accelerare a citirii datelor dintr-o foaie Excel - de la utilizarea MSXML și a altor biblioteci gata făcute la proceduri și funcții autonome. Ei bine, orice problemă poate fi rezolvată în mai multe moduri. Luați în considerare mai multe opțiuni și stabiliți care dintre opțiunile vor fi cele mai rapide. Ei bine, care opțiune va fi mai convenabilă - este deja toată lumea va decide pentru sine.


Citirea datelor din Excel

Mai întâi, luați în considerare opțiunea de citire a datelor prin care păcătoșii sunt cei care încep doar să se familiarizeze cu Excel în Delphi - citirea datelor din fiecare celulă separat. Procedura de testare cu această opțiune de citire ar putea arăta astfel:

procedura TForm16.SlowVariant;
var Rows, Cols, i, j: întreg;
Foaie de lucru: OLEVariant;
d: TDateTime;
începe
// deschideți cartea
ExcelApp.Workbooks.Open (edFile.Text);
// obțineți foaia activă
WorkSheet: = ExcelApp.ActiveWorkbook.ActiveSheet;
// determina numărul de rânduri și coloane ale tabelului
Rânduri: = WorkSheet.UsedRange.Rows.Count;
Cols: = WorkSheet.UsedRange.Columns.Count;

StringGrid1.RowCount: = Rânduri;
StringGrid1.ColCount: = Cols;

/ / marchează ora de începere a citirii
d: = Acum;

// trimiteți datele către tabel
pentru I: = 0 până la Rows-1
pentru j: = 0 până la Cols-1
StringGrid1.Cells [J, I]: = WorkSheet.UsedRange.Cells [I + 1, J + 1] Valoare;

Label2.Caption: = 'Timpul total de citire a paginii:' + FormatDateTime ('hh: mm: ss: zzz',
Acum () - d);
se încheie;

Contorul va conține, în cele din urmă, timpul de citire și de ieșire din datele StringGrid. Ar fi posibil să faci contorul exclusiv pentru citirea datelor dintr-o foaie, dar am decis să nu supraîncărc codul sursă cu variabile inutile. Dacă există o dorință - puteți să rescrieți un pic din codul sursă și să obțineți un timp de citire "curat".

Pentru a testa această opțiune, a fost creată o foaie Excel care conține 143 de rânduri și 142 de coloane cu date, adică 20306 celule cu date. Figura de mai jos arată valoarea contorului după citirea datelor:

12 secunde pentru a citi. dar dacă există 1000 de rânduri și 1000 de coloane? Deci nu puteți aștepta sfârșitul operației.

Dacă vă uitați atent la procedura prezentată mai sus, puteți observa că în buclă, de fiecare dată la fiecare iterație, obținem mai întâi intervalul ocupat de date, apoi în acest interval primim o anumită celulă și doar citim valoarea din celulă. De fapt, multe operațiuni inutile pentru citirea datelor dintr-o foaie nu sunt necesare. Mai ales atunci când datele sunt situate într-o matrice continuă. O opțiune de citire mai avantajoasă în acest caz este citirea datelor direct dintr-o gamă întreagă într-o matrice.

În practică, implementarea acestei versiuni a lucrării va fi chiar mai ușoară decât cea prezentată mai sus. Vezi pentru tine. Iată o opțiune de citire a datelor într-o întreagă gamă:

procedura TForm16.RangeRead;
var Rows, Cols, i, j: întreg;
Foaie de lucru: OLEVariant;
FData: OLEVariant;
d: TDateTime;
începe
// deschideți cartea
ExcelApp.Workbooks.Open (edFile.Text);
// obțineți foaia activă
WorkSheet: = ExcelApp.ActiveWorkbook.ActiveSheet;
// determina numărul de rânduri și coloane ale tabelului
Rânduri: = WorkSheet.UsedRange.Rows.Count;
Cols: = WorkSheet.UsedRange.Columns.Count;

// citiți întregul interval de date
FData: = WorkSheet.UsedRange.Value;

/ / marchează ora de începere a citirii
d: = Acum;

// trimiteți datele către tabel
pentru I: = 0 până la Rows-1
pentru j: = 0 până la Cols-1
StringGrid1.Cells [J, I]: = FData [I + 1, J + 1];

Label2.Caption: = 'Timpul total de citire a paginii:' + FormatDateTime ('hh: mm: ss: zzz',
Acum () - d);
se încheie;

Ne uităm la momentul operațiunii:

După cum puteți vedea, creșterea vitezei sa dovedit a fi enormă, chiar dacă timpul de actualizare a StringGrid a fost inclus în contor.

Aici ar fi indicat să se arate metoda inversă de lucru cu Excel, adică scrieți date într-o foaie de lucru Excel utilizând o matrice variantă.
Scrierea datelor în Excel

În cazul în care trebuie să scriem o cantitate mare de date pe o foaie Excel, trebuie să efectuăm o operație inversă, adică mai întâi creați o matrice variantă, apoi scrieți datele la această matrice și apoi scrieți întreaga matrice cu o singură operație în Excel. De exemplu, am scris o procedură care citește o cantitate mare de date dintr-un StringGrid și scrie aceste date în foaia a doua a unui registru de lucru deschis din Excel:

procedura TForm16.WriteData;
var i, j: întreg;
FData: varianta;
Sheet, Range: Varianta;
începe
// creați o variantă de matrice
FData: = VarArrayCreate ([1, StringGrid1.RowCount, 1, StringGrid1.ColCount], varVariant);
// completați matricele cu date din StringGrid
pentru i: = 1 la VarArrayHighBound (FData, 1)
pentru j: = 1 la VarArrayHighBound (FData, 2)
FData [i, j]: = StringGrid1.Cells [J-1, I-1];

// deschideți cartea
ExcelApp.Workbooks.Open (edFile.Text);
// Activați
Foaie: = ExcelApp.ActiveWorkBook.Sheets [2];
Sheet.Activate;
// selectați intervalul pentru introducerea datelor
Raza: Sheet.Range [Sheet.Cells [1,1], Sheet.Cells [VarArrayHighBound (FData, 1), VarArrayHighBound (FData, 2)];
// introduceți datele
Range.Value: = FData;
// afișați fereastra Excel
ExcelApp.Visible: = Adevărat;
se încheie;

Aici vom crea mai întâi o matrice de variante bidimensionale utilizând metoda VarArrayCreate, apoi vom umple matricea cu date și vom transmite acest matrice spre Excel. Rețineți că atunci când scrieți în Excel nu sunt folosite cicluri - înregistrarea are loc în 2 pași simpli:
selectați intervalul utilizând prima și ultima celulă
atribuiți o valoare domeniului din matrice.

Pentru a completa imaginea de mai jos, figura arată valoarea contorului, care a numărat timpul din momentul creării matricei pentru activarea aplicației Excel inclusiv:

Firește, cu creșterea cantității de date, timpul operației va crește, de asemenea. De exemplu, o foaie care conține 1000 de rânduri și 256 de coloane cu date a fost umplută aproximativ 7 secunde. Dacă acest timp este inacceptabil pentru tine, atunci procedura de mai sus poate fi ușor accelerată folosind perechea de metode VarArrayLock () și VarArrayUnLock (), dar trebuie să țineți cont de faptul că matricea FData va fi transpusă.

Ce altceva ar trebui să spun despre citirea / scrierea datelor în Excel. Probabil faptul că metodele de lucru de mai sus necesită în mod necesar programul Excel instalat pe computerul în care rulează programul. În legătură cu această circumstanță, este posibil să aveți nevoie de un mod mai universal de lucru cu Excel. Aici, din nou, pot exista mai multe opțiuni de lucru, dar voi arăta sau mai degrabă ar indica doar unul dintre ele - folosind biblioteca XLSReadWrite.

Un exemplu simplificat pentru Delphi 7
var
IntlXls: TXLSReadWriteII2;
I, J: Integer;

începe
// crea un obiect
IntlXls: = TXLSReadWriteII2.Creați (zero);

// titlul cărții
IntlXls.Sheets [0] .Name: = "Numele raportului meu";
// adăugați numărul necesar de rânduri și coloane
IntlXls.Sheets [0] .Rows.AddIfNone (0, 10000);
IntlXls.Sheets [0] .Columns.AddIfNone (0, 100);

// adăugați și introduceți lățimile celulelor (valoare în pixeli)
pentru I: = 0 până la 99 nu
IntlXls.Sheets [0] .Columns [I] .PixelWidth: = 150;
// introduceți înălțimea rândurilor (valoarea de aici nu este în pixeli, deci trebuie să ajustați)
pentru I: = 0 la 9999 nu
IntlXls.Sheets [0] .Rade [I] .Valoare: = 20 * 14;

// înființat
pentru J: = 0 până la 9999
pentru I: = 0 până la 99 nu
începe
// introduceți valoarea numerică
// dacă este necesar, de exemplu, să introduceți o linie, apoi folosiți AsString
IntlXls.Seturi [0] .AsFloat [I, J]: = J + I / 100;

// aliniere orizontală (chaLeft disponibil, chaCenter, chaRight)
IntlXls.Sheets [0] .Cell [I, J]. HorizAlignment: = chaLeft;

// aliniere verticală (disponibilă de la cvaTop, cvaCenter, cvaBottom)
IntlXls.Sheets [0] .Cel [I, J] .VerAlignment: = cvaTop;

// font
IntlXls.Sheets [0] .Cell [I, J] .FontName: = "Arial";
IntlXls.Sheets [0] Celula [I, J] .FontSize: = 12;
IntlXls.Steturi [0] .Cel [I, J] .FontStyle: = [];
IntlXls.Steturi [0] .Cel [I, J] .FontColor: = TColorToClosestXColor (clBlue);
IntlXls.Seturi [0] Celula [I, J] .Rotația: = 0;
// boldface
cu celulele IntlXls.La celule [I, J] fac
FontStyle: = FontStyle + [xfsBold];
// înclinat
cu celulele IntlXls.La celule [I, J] fac
FontStyle: = FontStyle + [xfsItalic];
// culoarea de fundal
IntlXls.Seturi [0] .Cel [I, J] .FillPatternForeColor: =
TColorToClosestXColor (clYellow);

// marginea din stânga (în mod similar restul granițelor)
IntlXls.Steturi [0] .Cel [I, J] .BorderLeftColor: =
TColorToClosestXColor (clBlack);
IntlXls.Sheets [0] .Cel [I, J] .BorderLeftStyle: = cbsThin;

// combinați celulele (aici două celule sunt combinate orizontal)
dacă aș avea 49 de ani
IntlXls.Seturi [0] .MergedCells.Add (I, J, I + 1, J);
se încheie;

IntlXls.SaveToFile ("c: \ demo.xls");
IntlXls.Free;
se încheie;

Un exemplu complet de colaborare cu biblioteca:

// aliniere orizontală
caz HorizAlign din
haLeft:
// Aliniere la stânga
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .HorizAlignment
: = chaLeft;
haCenter:
// alinierea centrului
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] HorizAlignment: =
chaCenter;
haRight:
// alinierea la dreapta
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .HorizAlignment
: = chaRight;
se încheie; // caz
// Aliniere verticală
caz VertAlign din
Vatop:
// Aliniere de sus
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .VerAlignment
: = cvaTop;
vaCenter:
// alinierea centrului
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .VerAlignment: =
cvaCenter;
vaBottom:
// Aliniere de jos
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .VerAlignment: =
cvaBottom;
se încheie; // caz
// font
IntlXls.Seturi [0] .Cell [IntlCol, IntlRow] .FontName: = Font.Name;
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .FontSize: = Font.Size;
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .FontCharset: =
Font.Charset;
IntlXls.Seturi [0] .Cell [IntlCol, IntlRow] .FontStyle: = [];
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .FontColor: =
TColorToClosestXColor (Font.Color);
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .Rotație: = Font.Angle;
// Există un semn de grăsime?
dacă este Font.IsBold atunci
// există
cu IntlXls.Sheets [0] Celula [IntlCol, IntlRow] face
FontStyle: = FontStyle + [xfsBold];
// există un semn înclinat?
dacă este Font.IsItalic atunci
// există
cu IntlXls.Sheets [0] Celula [IntlCol, IntlRow] face
FontStyle: = FontStyle + [xfsItalic];
// culoarea de fundal
dacă Color <> CLWind apoi
// culoarea este setată
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .FillPatternForeColor: =
TColorToClosestXColor (Culoare);
end // dacă
altfel
// activați doar celula (altfel este imposibil să adăugați borduri mai jos)
IntlXls.Sheets [0] .AsString [IntlCol, IntlRow]: = ";

// există o frontieră în stânga?
cu Borders.Left nu
dacă LineHeight> 0 atunci
// înființat
începe
// culoare
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderLeftColor: =
TColorToClosestXColor (Culoare);
// Grosime
dacă LineHeight = 1 atunci
// subțire
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderLeftStyle
: = cbsThin
altfel dacă LineHeight în [1, 2] atunci
// grosime medie
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderLeftStyle: =
cbsMedium
altfel
// grosime
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderLeftStyle
: = cbsFar;
se încheie; // dacă, cu
// frontiera de sus este?
cu Borders.Top face
dacă LineHeight> 0 atunci
// înființat
începe
// culoare
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderTopColor: =
TColorToClosestXColor (Culoare);
// Grosime
dacă LineHeight = 1 atunci
// subțire
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderTopStyle
: = cbsThin
altfel dacă LineHeight în [1, 2] atunci
// grosime medie
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderTopStyle: =
cbsMedium
altfel
// grosime
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderTopStyle
: = cbsFar;
se încheie; // dacă, cu
// frontiera din dreapta este?
cu Borders.Right face
dacă LineHeight> 0 atunci
// înființat
începe
// culoare
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderRightColor: =
TColorToClosestXColor (Culoare);
// Grosime
dacă LineHeight = 1 atunci
// subțire
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderRightStyle
: = cbsThin
altfel dacă LineHeight în [1, 2] atunci
// grosime medie
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderRightStyle: =
cbsMedium
altfel
// grosime
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderRightStyle
: = cbsFar;
se încheie; // dacă, cu
// frontiera de dedesubt?
cu Borders.Bottom
dacă LineHeight> 0 atunci
// înființat
începe
// culoare
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderBottomColor: =
TColorToClosestXColor (Culoare);
// Grosime
dacă LineHeight = 1 atunci
// subțire
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderBottomStyle
: = cbsThin
altfel dacă LineHeight în [1, 2] atunci
// grosime medie
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderBottomStyle: =
cbsMedium
altfel
// grosime
IntlXls.Sheets [0] .Cell [IntlCol, IntlRow] .BorderBottomStyle
: = cbsFar;
se încheie; // dacă, cu
// trebuie să fuzionați?
dacă ((Intervalul de lățime> 1) sau (Intervalul de înălțime> 1)) și
((IntlMainCol = IntlCol) și (IntlMainRow = IntlRow)) apoi
// merge
IntlXls.Sheets [0] .MergedCells.Add (IntlCol, IntlRow,
IntlCol + Range.Width - 1, IntlRow + Range.Height - 1);
// a făcut clic pe utilizator butonul de întrerupere a exportului?
dacă btnCancel.Tag = 2 atunci
Da, plecăm
Break;
se încheie; // cu
se încheie; // pentru
// actualizați starea
prgrbrStatus.Position: = prgrbrStatus.Poziția + 1;
Application.ProcessMessages;
// a făcut clic pe utilizator butonul de întrerupere a exportului?
dacă btnCancel.Tag = 2 atunci
Da, plecăm
Break;
se încheie; // pentru
// a făcut clic pe utilizator butonul de întrerupere a exportului?
dacă btnCancel.Tag <> 2 atunci
// nr
începe
// în colțul din stânga sus
IntlXls.Sheet [0] .TopRow: = 0;
IntlXls.Sheet [0] .LeftCol: = 0;
IntlXls.Sheet [0] .Selecție.ActivitateRo: = 0;
IntlXls.Sheet [0] .Selecție.Activ.Col: = 0;

// status
prgrbrStatus.Position: = prgrbrStatus.Max;
Application.ProcessMessages;
// scrieți la dosar
IntlXls.FileName: = AFileName;
IntlXls.Write;
// totul a avut succes
Rezultat: = UNIRPT_OK;

end // dacă
altfel
// Da
Rezultat: = UNIRPT_GENERATE_ABORT;

în cele din urmă
// eliberați memoria
IntlXls.Free;
se încheie; // try..finally
se încheie; // funcția ExportToExcelXls

Articole similare