Mega Code Archive

 
Categories / Delphi / Files
 

A simple File Comparison Utility

Title: A simple File Comparison Utility Question: Sometimes you are only interested in knowing if two files are the same- you might have hit return a few times in the editor so one looks bigger than the other and has a later save date but it might be the same otherwise... Answer: The utility listed below (both .pas and .dfm source) accepts as input two file names. For convenience these filenames (with associated paths) are saved out between runs and you can copy the filename from the first box to the second (click the red down arrow) - it combines the first filename with the existing 2nd path. Both edit boxes allow you to browse for files. File comparison is simple and fast. Each file is read into a memory stream and then a count of each of the 256 possible characters is made. You could argue that by cutting and moving text elsewhere in a text file file that this would break my method (as char counts would be unaffected) and you'd be right but I think for most purposes this method is probably sufficient and it works with binary as well as text files. I realise a CRC calculation could also be added- feel free to do so. When there are differences the output is a string showing each character value (0-255) followed by the count in brackets. For something thrown together quickly in an hour or so, it has served me well and compares files of a few megabytes pretty quickly. Pascal Source unit viewdiff; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons; type TDBDiff = class Ffilename1 : string; FfileName2 : string; FBuff1 : TmemoryStream; FBuff2 : TmemoryStream; FCounts1 : array[0..255] of integer; FCounts2 : array[0..255] of integer; FProcessed : boolean; fDifferenceStr : string; FDifferent: boolean; private function GetDiffCount(ch: char): integer; function GetDifferences: boolean; procedure Clear; procedure BuildDiffTable(Mem1,Mem2 : pointer;size1,size2:integer); procedure BuildDifferenceStr; function CheckIfSame: boolean; public Constructor Create; Destructor Destroy;override; property Different : boolean read GetDifferences; property DifferenceStr : string read fDifferenceStr; property DiffCount[ch : char] : integer read GetDiffCount; property Filename1 : string read FFilename1 write FFilename1; property Filename2 : string read FFilename2 write FFilename2; end; // TdbDiff TForm1 = class(TForm) Button1: TButton; Button2: TButton; fileopen: TOpenDialog; Edit1: TEdit; Edit2: TEdit; GoBtn: TButton; btnCopyDown: TBitBtn; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure GoBtnClick(Sender: TObject); procedure FormActivate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure btnCopyDownClick(Sender: TObject); private procedure CheckGoBtn; procedure LoadEditBoxes; procedure SaveEditBoxes; { Private declarations } public { Public declarations } Aftercreate : boolean; Diff : tDbDiff; StartPath : string; end; var Form1: TForm1; implementation {$R *.DFM} Const editsavefilename = 'diff.ini'; CrLf = #13#10; procedure TForm1.Button1Click(Sender: TObject); begin if Edit1.Text '' then FileOpen.Initialdir := ExtractFileDir(Edit1.Text); if FileOpen.execute then edit1.Text := FileOpen.Filename; CheckGobtn; end; procedure Tform1.checkGoBtn; var filename :string; begin Gobtn.enabled := false; Filename := trim(Edit1.Text); if (Filename '') and fileexists(Filename) then begin Filename := trim(Edit2.Text); if (Filename '') and fileexists(Filename) then GoBtn.Enabled := True; end; end; procedure TForm1.Button2Click(Sender: TObject); begin if Edit2.Text '' then FileOpen.Initialdir := ExtractFileDir(Edit2.Text); if FileOpen.execute then edit2.Text := FileOpen.Filename; CheckGoBtn; end; procedure TForm1.FormCreate(Sender: TObject); begin Aftercreate := true; end; { TDBDiff } procedure TdbDiff.Clear; begin fFilename1 := ''; fFilename2 := ''; fillchar(fCounts1,sizeof(fcounts1),0); fillchar(fCounts2,sizeof(fcounts2),0); fDifferenceStr := ''; fProcessed := false; end; constructor TDBDiff.Create; begin fBuff1 := TmemoryStream.Create; fBuff2 := TmemoryStream.Create; Clear; end; destructor TDBDiff.Destroy; begin FBuff2.Free; FBuff1.Free; end; function TDBDiff.GetDiffCount(ch: char): integer; begin result := fCounts1[ord(ch)]-Fcounts2[ord(ch)]; end; procedure TdbDiff.BuildDifferenceStr; var Index : integer; begin fDifferenceStr := ''; for Index := 0 to 255 do if fcounts1[Index] fCounts2[Index] then begin fDifferenceStr := fDifferencestr + ' #'+inttostr(Index)+'('+inttostr(fcounts1[Index]-Fcounts2[Index])+')'; end; end; function TDBDiff.CheckIfSame: boolean; var Index : integer; begin result := true; for Index := 0 to 255 do if fcounts1[Index]fcounts2[Index] then begin Result := false; exit; end; end; procedure TDBDiff.BuildDiffTable(mem1,mem2 : pointer;size1,size2:integer); type Bytemap = array[0..2000000000] of byte; BytemapPtr = ^ByteMap; var MapPtr : ByteMapPtr; Index : integer; begin MapPtr := ByteMapPtr(mem1); for Index := 0 to size1-1 do inc(fcounts1[MapPtr^[Index]]); MapPtr := ByteMapPtr(mem2); for Index := 0 to size2-1 do inc(fcounts2[MapPtr^[Index]]); end; function TDBDiff.GetDifferences: boolean; var fs : TFileStream; begin if fProcessed then Result := Fdifferent else begin Result := false; if (trim(Ffilename1) ='') or (trim(FFilename2) = '') then exit; fProcessed := true; fs := TfileStream.Create(fFilename1,fmOpenRead); fbuff1.LoadFromStream(fs); fs.free; fs := TfileStream.Create(fFilename2,fmOpenRead); fbuff2.LoadFromStream(fs); fs.free; BuildDiffTable(fbuff1.memory,fbuff2.memory,fbuff1.size,fbuff2.size); BuildDifferenceStr; Result := not CheckIfSame; end; end; procedure TForm1.GoBtnClick(Sender: TObject); begin diff.Clear; diff.Filename1 := edit1.text; diff.Filename2 := edit2.text; if diff.Different then ShowMessage( 'Differences between '+ Crlf + diff.Filename1 + Crlf + diff.Filename2 + Crlf + Crlf + diff.DifferenceStr) else ShowMessage('Files identical'); end; procedure TForm1.FormActivate(Sender: TObject); begin if AfterCreate then begin AFterCreate := false; diff := tdbdiff.Create; GetDir(0,StartPath); if StartPath[Length(StartPath)] '\' then StartPath := StartPath + '\'; LoadEditBoxes; end; end; procedure Tform1.LoadEditBoxes; var tf : textfile; s : string; begin if fileexists(StartPath+EditSaveFilename) then begin assignfile(tf,StartPath+EditSavefilename); reset(tf); try readln(tf,s); edit1.text := s; readln(tf,s); edit2.text := s; finally Closefile(Tf); CheckGoBtn; end; end; end; procedure Tform1.SaveEditBoxes; var tf : textfile; s : string; begin assignfile(tf,StartPath+EditSavefilename); rewrite(tf); try s := edit1.text; writeln(tf,s); s := edit2.text; writeln(tf,s); finally Closefile(Tf); end; end; procedure TForm1.FormDestroy(Sender: TObject); begin SaveEditBoxes; end; procedure TForm1.btnCopyDownClick(Sender: TObject); begin edit2.text := ExtractFileDir(Edit2.Text)+ '\' + ExtractFileName(Edit1.Text); end; end. .DFM Source object Form1: TForm1 Left = 338 Top = 555 Width = 462 Height = 172 Caption = 'Difference Utility' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False OnActivate = FormActivate OnCreate = FormCreate OnDestroy = FormDestroy PixelsPerInch = 96 TextHeight = 13 object Button1: TButton Left = 12 Top = 30 Width = 75 Height = 25 Caption = '1st File' TabOrder = 0 OnClick = Button1Click end object Button2: TButton Left = 12 Top = 78 Width = 75 Height = 25 Caption = '2nd File' TabOrder = 1 OnClick = Button2Click end object Edit1: TEdit Left = 96 Top = 30 Width = 343 Height = 21 Anchors = [akLeft, akTop, akRight] TabOrder = 2 end object Edit2: TEdit Left = 96 Top = 78 Width = 343 Height = 21 Anchors = [akLeft, akTop, akRight] TabOrder = 3 end object GoBtn: TButton Left = 96 Top = 114 Width = 75 Height = 25 Caption = 'Compare' Enabled = False TabOrder = 4 OnClick = GoBtnClick end object btnCopyDown: TBitBtn Left = 240 Top = 54 Width = 26 Height = 23 TabOrder = 5 OnClick = btnCopyDownClick Glyph.Data = { 76010000424D7601000000000000760000002800000020000000100000000100 0400000000000001000000000000000000001000000010000000000000000000 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000 FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333303333 333333333337F33333333333333033333333333333373F333333333333090333 33333333337F7F33333333333309033333333333337373F33333333330999033 3333333337F337F33333333330999033333333333733373F3333333309999903 333333337F33337F33333333099999033333333373333373F333333099999990 33333337FFFF3FF7F33333300009000033333337777F77773333333333090333 33333333337F7F33333333333309033333333333337F7F333333333333090333 33333333337F7F33333333333309033333333333337F7F333333333333090333 33333333337F7F33333333333300033333333333337773333333} NumGlyphs = 2 end object fileopen: TOpenDialog DefaultExt = '*' Filter = 'Any File|*.*' Left = 354 end end