home *** CD-ROM | disk | FTP | other *** search
- unit Fc_form;
-
- interface
-
- uses
- SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
- Forms, Dialogs, StdCtrls, Grids, Spin, Buttons, ExtCtrls;
-
- type
- TDateivergleich = class(TForm)
- Grid: TStringGrid;
- Panel1: TPanel;
- UpBitBtn: TBitBtn;
- HScrollBar: TScrollBar;
- Panel2: TPanel;
- VScrollBar: TScrollBar;
- DownBitBtn: TBitBtn;
- Panel3: TPanel;
- procedure FormCreate(Sender: TObject);
- procedure GridDrawCell(Sender: TObject; Col, Row: Longint; Rect: TRect; State: TGridDrawState);
- procedure FormResize(Sender: TObject);
- procedure DownBitBtnClick(Sender: TObject);
- procedure UpBitBtnClick(Sender: TObject);
- procedure HScrollBarChange(Sender: TObject);
- procedure VScrollBarChange(Sender: TObject);
- private
- Datei : array[1..2] of Textfile; { Die beiden Textdateien; Datei[1] wird in der ersten
- Spalte von 'Grid' angezeigt, Datei[2] in der zweiten. }
- Last : array[1..2] of Integer; { 'Last[i]' ist die letzte belegte Zeile in der Spalte
- i von 'Grid'. Darin steht also die letzte aus Datei[i]
- gelesene Zeile. }
- sync : Integer; { Die letzte Zeile von 'Grid', in der die beiden Spalten
- ⁿbereinstimmen. }
- Nr: Integer; { Die aktuelle Zeilennummer (die in der Spalte 0 von 'Grid'
- angezeigt wird. }
- procedure SetGridWidth;
- procedure ReadLines(col,n:Integer);
- procedure InsertEmptyCells(col,row,num:Integer);
- procedure Beenden;
- function ReadUntilDiff : Boolean;
- function SearchLines(c1, r1, c2, r2a, r2b, n: Integer): Integer;
- function ReSync : Boolean;
-
- function Vorgaenger: Integer;
- function Nachfolger: Integer;
- public
- procedure Dateien_vergleichen(const Name1,Name2:string);
- end;
-
- var
- Dateivergleich: TDateivergleich;
-
- implementation
-
- {$R *.DFM}
-
- procedure TDateivergleich.FormCreate(Sender: TObject);
- begin
- Last[1] := 0;
- Last[2] := 0;
- sync := 0;
- Nr := 0;
- Grid.Canvas.Font := Grid.Font;
- Grid.Cells[0,0] := 'Zeile';
- Grid.Cells[1,0] := '';
- Grid.Cells[2,0] := '';
- SetGridWidth;
- { Schalter und Scrollbars ausrichten }
- Panel3.Width := Grid.ColWidths[0];
- UpBitBtn.Align := alRight;
- DownBitBtn.Align := alBottom;
- HScrollBar.Align := alClient;
- VScrollBar.Align := alClient;
- end;
-
-
- { ==================== Methoden zur Darstellung der Dateien ==================== }
-
- procedure TDateivergleich.SetGridWidth;
- var
- i, j: Integer;
- begin
- i := Grid.Width - Grid.GridLineWidth;
- j := Grid.Canvas.TextWidth(Grid.Cells[0,0]);
- Grid.ColWidths[0] := j+2;
- Grid.ColWidths[1] := (i-j-2) div 2;
- Grid.ColWidths[2] := i - (j+2) - (i-j-2) div 2;
- Grid.DefaultRowHeight := Grid.Canvas.TextHeight(Grid.Cells[0,0]);
- end;
-
-
- procedure TDateivergleich.FormResize(Sender: TObject);
- begin
- SetGridWidth;
- DownBitBtn.Enabled := Nachfolger>0;
- UpBitBtn.Enabled := (Grid.TopRow>1) and (Vorgaenger>0);
- end;
-
-
- { ---------- Zelle des Gitters 'Grid' zeichnen.
- Besonderheit ist hier, da▀ eine Zeile hervorgehoben dargestellt wird, falls in
- Spalte 0 entweder nichts steht oder der String mit einem 'x' beginnt.
- Das 'x' in Spalte 0 wird nicht angezeigt. }
-
- procedure TDateivergleich.GridDrawCell(Sender: TObject; Col, Row: Longint;
- Rect: TRect; State: TGridDrawState);
- var
- Highlight: Boolean;
- begin
- Highlight := (Grid.Cells[0,Row]<>'') and (Grid.Cells[0,Row][1]='x');
- with Grid.Canvas do begin
- Brush.Style := bsSolid;
- if Highlight then
- Brush.Color := clTeal
- else if gdFixed in State then
- Brush.Color := clBtnFace
- else
- Brush.Color := clWindow;
- FillRect(Rect);
-
- if Highlight then
- Font.Color := clBlack
- else
- Font.Color := clGray;
- if (Col=0) and (Row>0) then
- TextOut (Rect.Left+1, Rect.Top, Copy(Grid.Cells[Col,Row],2,20))
- else if Row=0 then
- TextOut (Rect.Left+1, Rect.Top, Grid.Cells[Col,Row])
- else
- TextOut (Rect.Left+1, Rect.Top, Copy(Grid.Cells[Col,Row],HScrollBar.Position+1,255));
-
- if Col=1 then begin
- Pen.Color := clBtnFace;
- Pen.Style := psSolid;
- Pen.Width := 2;
- Rect.Right := Rect.Right - 1;
- MoveTo (Rect.Right, Rect.Top);
- LineTo (Rect.Right, Rect.Bottom);
- end;
- end;
- end;
-
-
- { ==================== Methoden zum Dateivergleich ==================== }
-
- { ---------- Zeilen einlesen
- Diese Methode liest 'n' Zeilen aus der Datei 'Datei[col]' ein und
- fⁿgt sie in das Gitter ein. Entsprechend wird 'Last[col]' erh÷ht.
- Falls Zeilen aus der ersten Datei gelesen werden, dann werden auch die
- Zeilennummern in Spalte 0 "fortgeschrieben". }
-
- procedure TDateivergleich.ReadLines(col,n:Integer);
- var
- Line: string;
- begin
- while not EoF(Datei[col]) and (n>0) do begin
- Readln(Datei[col], Line);
- Inc(Last[col]);
- if Last[col]>=Grid.RowCount then
- Grid.RowCount := Last[col]+1;
- if Col=1 then begin
- Inc(Nr);
- { Beachten: Das erste Zeichen im String von Spalte 0 wird nicht angezeigt sondern dient
- als Kennzeichnung, ob die Zeile hervorgehoben werden soll. A priori soll sie das nicht,
- darum ist das erste Zeichen ' '. }
- Grid.Cells[0,Last[col]] := ' '+IntToStr(Nr);
- end;
- Grid.Cells[col,Last[col]] := Line;
- Dec(n);
- end;
- end;
-
-
- { ---------- Vergleichsoperationen }
-
- procedure TDateivergleich.InsertEmptyCells(col,row,num:Integer);
- var
- i: Integer;
- begin
- if Last[col]+num>=Grid.RowCount then
- Grid.RowCount := Last[col]+num+1;
- for i := Last[col] downto row do begin
- Grid.Cells[col,i+num] := Grid.Cells[col,i];
- Grid.Cells[col,i] := '';
- if col=1 then begin
- Grid.Cells[0,i+num] := Grid.Cells[0,i];
- Grid.Cells[0,i] := 'x';
- end;
- end;
- Inc(Last[col],num);
- if Last[col]>=Grid.RowCount then
- Grid.RowCount := Last[col]+1;
- end;
-
-
- { ---------- Vergleich beenden
- Wenn aus irgendeinem Grund nicht mehr weiter syncronisier werden kann, dann schlie▀t
- diese Methode den Dateivergleich ab: Alle Zeilen nach 'sync' werden als abweichend
- markiert. }
-
- procedure TDateivergleich.Beenden;
- var
- p: Integer;
- begin
- if not EoF(Datei[1]) then
- ReadLines(1,30000);
- if not EoF(Datei[2]) then
- ReadLines(2,30000);
- for p := sync+1 to Grid.RowCount do
- Grid.Cells[0,p] := 'x' + Copy(Grid.Cells[0,p],2,20);
- end;
-
-
- { ---------- Solange Zeilenweise mit der Marke 'sync' weiterlaufen, bis eine Abweichung entdeckt
- wird. 'sync' wird dann auf die letzte ⁿbereinstimmende Zeile gesetzt.
- Falls ein Syncronisieren der Dateien sicher nicht m÷glich ist (weil mindestens
- eine der Dateien endet), dann wird 'False' sonst 'True' zurⁿckgegeben. }
-
- function TDateivergleich.ReadUntilDiff : Boolean;
- begin
- result := False;
- if (not EoF(Datei[1]) or (sync<Last[1])) and (not EoF(Datei[2]) or (sync<Last[2])) then begin
- repeat
- if sync=Last[1] then ReadLines(1,1);
- if sync=Last[2] then ReadLines(2,1);
- Inc(sync);
- until (EoF(Datei[1]) and (sync=Last[1])) or (EoF(Datei[2]) and (sync=Last[2]))
- or (Grid.Cells[1,sync]<>Grid.Cells[2,sync]);
- { Jetzt gibt's zwei M÷glichkeiten
- 1) Es wurde eine Abweichung gefunden und es sind noch Zeilen in den Dateien, so das ein
- Versuch zum Syncronisieren gestartet werden kann.
- 2) Die Schleife wurde beendet, aber weil in einer Datei keine Zeilen mehr sind. }
- if (EoF(Datei[1]) and (sync=Last[1])) or (EoF(Datei[2]) and (sync=Last[2])) then begin
- if Grid.Cells[1,sync]<>Grid.Cells[2,sync] then
- Dec(sync);
- end else begin
- Dec(sync);
- result := True;
- end;
- end;
- end;
-
-
- function TDateivergleich.SearchLines(c1, r1, c2, r2a, r2b, n: Integer): Integer;
- var
- k: Integer;
- begin
- repeat
- k := 0;
- while (k<n) and (Grid.Cells[c1,r1+k]=Grid.Cells[c2,r2a+k]) do
- Inc(k);
- Inc(r2a);
- until (k=n) or (r2a>r2b);
- if k=n then
- result := r2a-1
- else
- result := -1;
- end;
-
-
- function TDateivergleich.ReSync : Boolean;
- const
- k = 2;
- inv : array[1..2] of Integer = (2, 1);
- var
- M : array[1..2] of Integer;
- Ende : array[1..2] of Boolean;
- c, p: Integer;
- begin
- result := True;
- { Die beiden "Marker" M[1] und M[2] geben an, bis zu welcher Zeile im Gitter das Programm
- schaut, um 'k' ⁿbereinstimmende Zeilen nach der Zeile 'sync' zu finden. }
- M[1] := sync+k;
- M[2] := sync+k;
- { Im Gitter mⁿssen sich mindestens soviele Zeilen finden, wie die Marker angeben; ggf. mⁿssen
- weitere Zeilen eingelesen werden. Es ist aber (durch vorangegangenes Einfⁿgen von Zeilen) auch
- m÷glich, da▀ sich in einer oder beiden Spalten schon genug oder sogar mehr Zeilen befinden. }
- if M[1]>Last[1] then ReadLines(1,M[1]-Last[1]);
- if M[2]>Last[2] then ReadLines(2,M[2]-Last[2]);
- { Wenn eine der Dateien nicht mehr genug Zeilen enthΣlt, dann mu▀ die Suche abgebrochen werden. }
- if (M[1]>Last[1]) or (M[2]>Last[2]) then begin
- result := False;
- Exit;
- end;
-
- Ende[1] := (M[1]=Last[1]) and EoF(Datei[1]);
- Ende[2] := (M[2]=Last[2]) and EoF(Datei[2]);
- if Ende[1] then c := 2 else c := 1;
- p := -1;
-
- while (p<0) and (not Ende[1] or not Ende[2]) do begin
- { Marker eine Zeile weitersetzen; ggf. eine Zeile einlesen. }
- Inc(M[c]);
- if M[c]>Last[c] then ReadLines(c,1);
- { Testen, ob das Ende der Datei erreicht ist. }
- Ende[c] := (M[c]=Last[c]) and EoF(Datei[c]);
- { Prⁿfen, ob syncronisiert werden kann. }
- p := SearchLines(c,M[c]-k+1, inv[c],sync+1,M[inv[c]]-k+1, k);
- { Wenn das Ende der anderen Datei noch nicht erreicht ist: ▄bergeben }
- if (p<0) and not Ende[inv[c]] then
- c := inv[c];
- end;
-
- if p>0 then begin
- if M[c]-k+1-p>0 then
- InsertEmptyCells(inv[c],p,M[c]-k+1-p);
- for p := sync+1 to M[c]-k do
- Grid.Cells[0,p] := 'x' + Copy(Grid.Cells[0,p],2,20);
- sync := M[c];
- end else begin
- for p := sync+1 to Grid.RowCount do
- Grid.Cells[0,p] := 'x' + Copy(Grid.Cells[0,p],2,20);
- end;
- end;
-
-
- procedure TDateivergleich.Dateien_vergleichen(const Name1,Name2:string);
- var
- weiter: Boolean;
- begin
- Screen.Cursor := crHourglass;
- try
- AssignFile(Datei[1],Name1);
- Reset(Datei[1]);
- AssignFile(Datei[2],Name2);
- Reset(Datei[2]);
- Grid.Cells[1,0] := AnsiLowerCase(ExtractFileName(Name1));
- Grid.Cells[2,0] := AnsiLowerCase(ExtractFileName(Name2));
-
- weiter := True;
- while weiter and (not EoF(Datei[1]) or not EoF(Datei[2])) do begin
- weiter := ReadUntilDiff;
- if weiter and (not EoF(Datei[1]) or not EoF(Datei[2])) then
- weiter := ReSync;
- end;
- if not weiter then
- Beenden;
-
- CloseFile(Datei[1]);
- CloseFile(Datei[2]);
- finally
- Screen.Cursor := crDefault;
- if Grid.RowCount>2 then VScrollBar.Max := Grid.RowCount-2 else VScrollBar.Max := 1;
- end;
- end;
-
-
- { ==================== Methoden zur Ereignisbehandlung ==================== }
-
- function TDateivergleich.Nachfolger: Integer;
- var
- r: Integer;
- begin
- r := Grid.TopRow + Grid.VisibleRowCount div 2;
- while (r<Grid.RowCount) and (Grid.Cells[0,r]<>'') and (Grid.Cells[0,r][1]='x') do
- Inc(r);
- while (r<Grid.RowCount) and ((Grid.Cells[0,r]='') or (Grid.Cells[0,r][1]=' ')) do
- Inc(r);
- if r<Grid.RowCount then result := r else result := -1;
- end;
-
-
- procedure TDateivergleich.DownBitBtnClick(Sender: TObject);
- var
- r: Integer;
- begin
- r := Nachfolger;
- if r>0 then begin
- r := r - Grid.VisibleRowCount div 2;
- if r>1 then VScrollBar.Position := r else VScrollBar.Position := 1;
- end;
- end;
-
-
- function TDateivergleich.Vorgaenger: Integer;
- var
- r: Integer;
- begin
- r := Grid.TopRow + Grid.VisibleRowCount div 2;
- while (r>0) and (Grid.Cells[0,r]<>'') and (Grid.Cells[0,r][1]='x') do
- Dec(r);
- while (r>0) and ((Grid.Cells[0,r]='') or (Grid.Cells[0,r][1]=' ')) do
- Dec(r);
- result := r;
- end;
-
-
- procedure TDateivergleich.UpBitBtnClick(Sender: TObject);
- var
- r: Integer;
- begin
- r := Vorgaenger;
- if r>0 then begin
- r := r - Grid.VisibleRowCount div 2;
- if r>1 then VScrollBar.Position := r else VScrollBar.Position := 1;
- end;
- end;
-
-
- procedure TDateivergleich.HScrollBarChange(Sender: TObject);
- begin
- Grid.Repaint;
- end;
-
- procedure TDateivergleich.VScrollBarChange(Sender: TObject);
- begin
- Grid.TopRow := VScrollBar.Position;
- DownBitBtn.Enabled := Nachfolger>0;
- UpBitBtn.Enabled := (Grid.TopRow>1) and (Vorgaenger>0);
- end;
-
- end.
-