Compare two files and find the differences (Views: 27)
Problem/Question/Abstract: Does anyone know of sample code that compares two files and indicates if there is a difference or not (like FC)? I could use filestreams and compare bit by bit but I'm hoping someone has done this in a more optimized fashion. Answer: Use CreateFileMapping and compare pointers. Look at my unfinished unit below for an example: unit findin; interface uses Windows, SysUtils, findstr; type TFindInFile = class; TFindIn = class protected FFindInFile: TFindInFile; FHandle: THandle; function GetPartNum: Integer; virtual; abstract; function GetPartLen(Index: Integer): Cardinal; virtual; abstract; public constructor Create(FindInFile: TFindInFile; FileName: string); virtual; destructor Destroy; override; function CanUseMem: Boolean; virtual; abstract; function UseMemSize: Cardinal; virtual; abstract; function GetPart(Index: Integer; Len: Cardinal): Pointer; virtual; abstract; property PartNum: Integer read GetPartNum; property PartLen[Index: Integer]: Cardinal read GetPartLen; end; TFindInClass = class of TFindIn; TBMSearchFunc = function(var Buffer; BufLength: Cardinal; var BT: TBMTbl; MatchString: PAnsiChar; var Pos: Cardinal): Boolean; TFindInFile = class protected FFindIn: TFindIn; FFindInClass: TFindInClass; FFindStrParams: PFindStrParams; FMemHandle: THandle; FMem: Pointer; FStrLen: Cardinal; FDriveTp: UINT; FBMSearchFunc: TBMSearchFunc; function GetDriveTp(Root: string): UINT; public constructor Create(FindStrParams: PFindStrParams); destructor Destroy; override; function Find(FileName: string): Cardinal; function SwitchToRoot(Root: string): Boolean; virtual; end; TFindInHDD = class(TFindIn) private FSize: Cardinal; protected FMapPtr: Pointer; function GetPartNum: Integer; override; function GetPartLen(Index: Integer): Cardinal; override; public constructor Create(FindInFile: TFindInFile; FileName: string); override; destructor Destroy; override; function CanUseMem: Boolean; override; function UseMemSize: Cardinal; override; function GetPart(Index: Integer; Len: Cardinal): Pointer; override; end; PIntArr = ^TIntArr; TIntArr = array[0..1] of Cardinal; TFindInRemovable = class(TFindIn) private FSize: Cardinal; protected FPartNum: Integer; function GetPartNum: Integer; override; function GetPartLen(Index: Integer): Cardinal; override; public constructor Create(FindInFile: TFindInFile; FileName: string); override; function CanUseMem: Boolean; override; function UseMemSize: Cardinal; override; function GetPart(Index: Integer; Len: Cardinal): Pointer; override; end; implementation resourcestring SInvalidDrive = 'Invalid drive - "%s".'; { TFindIn } constructor TFindIn.Create(FindInFile: TFindInFile; FileName: string); begin inherited Create; FFindInFile := FindInFile; FHandle := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0); if FHandle = INVALID_HANDLE_VALUE then RaiseLastWin32Error; end; destructor TFindIn.Destroy; begin if FHandle <> 0 then CloseHandle(FHandle); inherited Destroy; end; { TFindInHDD } constructor TFindInHDD.Create(FindInFile: TFindInFile; FileName: string); var hFile: THandle; begin inherited Create(FindInFile, FileName); FSize := GetFileSize(FHandle, nil); hFile := CreateFileMapping(FHandle, nil, PAGE_READONLY, 0, 0, nil); CloseHandle(FHandle); FHandle := hFile; if FHandle <> 0 then begin FMapPtr := MapViewOfFile(FHandle, FILE_MAP_READ, 0, 0, 0); if FMapPtr = nil then RaiseLastWin32Error; end else RaiseLastWin32Error; end; destructor TFindInHDD.Destroy; begin if FMapPtr <> nil then UnmapViewOfFile(FMapPtr); inherited Destroy; end; function TFindInHDD.GetPartNum: Integer; begin Result := 1; end; function TFindInHDD.GetPartLen(Index: Integer): Cardinal; begin Result := FSize; end; function TFindInHDD.GetPart(Index: Integer; Len: Cardinal): Pointer; begin Result := FMapPtr; end; function TFindInHDD.CanUseMem: Boolean; begin Result := False; end; function TFindInHDD.UseMemSize: Cardinal; begin Result := 0; end; { TFindInRemovable } constructor TFindInRemovable.Create(FindInFile: TFindInFile; FileName: string); var S: Cardinal; begin inherited Create(FindInFile, FileName); FSize := GetFileSize(FHandle, nil); if FSize = $FFFFFFFF then RaiseLastWin32Error; S := UseMemSize - Pred(FFindInFile.FStrLen); FPartNum := FSize div S; if FSize mod S <> 0 then Inc(FPartNum); end; function TFindInRemovable.GetPartNum: Integer; begin Result := FPartNum; end; function TFindInRemovable.GetPartLen(Index: Integer): Cardinal; begin Result := UseMemSize; if (Index = Pred(FPartNum)) and (FSize mod (Result - FFindInFile.FStrLen) <> 0) then Result := FSize - (Result - Pred(FFindInFile.FStrLen)) * Pred(FPartNum); end; function TFindInRemovable.GetPart(Index: Integer; Len: Cardinal): Pointer; var Dist: ULONG; Reading: DWORD; begin Result := FFindInFile.FMem; Dist := Index * (UseMemSize - Pred(FFindInFile.FStrLen)); SetFilePointer(FHandle, Dist, nil, FILE_BEGIN); if not ReadFile(FHandle, Result^, Len, Reading, nil) then RaiseLastWin32Error; end; function TFindInRemovable.CanUseMem: Boolean; begin Result := True; end; function TFindInRemovable.UseMemSize: Cardinal; begin Result := 8; {512 * 1024;} end; { TFindInFile } function Max(V1, V2: Integer): Integer; assembler; register; asm CMP EAX,EDX JG @@1 MOV EAX,EDX @@1: end; constructor TFindInFile.Create(FindStrParams: PFindStrParams); var I: Integer; begin inherited Create; FDriveTp := $FFFFFFFF; FFindStrParams := FindStrParams; if FFindStrParams^.CaseSensitive then FBMSearchFunc := BMSearch else FBMSearchFunc := BMSearchUC; FStrLen := 0; for I := 0 to Pred(FFindStrParams^.Substr.Count) do FStrLen := Max(FStrLen, length(FFindStrParams^.Substr[I])); end; destructor TFindInFile.Destroy; begin if FMemHandle <> 0 then begin GlobalUnlock(FMemHandle); GlobalFree(FMemHandle); end; inherited Destroy; end; function TFindInFile.GetDriveTp(Root: string): UINT; begin Result := GetDriveType(PChar(ExtractFileDrive(Root) + '\')); end; function TFindInFile.Find(FileName: string): Cardinal; var I, J, K: Integer; L: Cardinal; P: Pointer; PI: PFindStrInfo; BMSFunc: TBMSFunc; begin Result := NotFound; FFindIn := FFindInClass.Create(Self, FileName); try if FFindIn.CanUseMem and (FMem = nil) then begin FMemHandle := GlobalAlloc(GMEM_MOVEABLE, FFindIn.UseMemSize); if FMemHandle = 0 then RaiseLastWin32Error; FMem := GlobalLock(FMemHandle); end; for I := 0 to Pred(FFindIn.PartNum) do for J := 0 to Pred(FFindStrParams^.Substr.Count) do begin L := FFindIn.PartLen[I]; P := FFindIn.GetPart(I, L); Result := FindString(P^, L, J, FFindStrParams); PI := PFindStrInfo(FFindStrParams.Substr.Objects[J]); if FBMSearchFunc(P^, L, PI^.BMTbl, PI^.FindS, Result) then begin if I > 0 then for K := 1 to I - 1 do Inc(Result, FFindIn.PartLen[K]); Exit; end; end; finally FFindIn.Free; end; end; function TFindInFile.SwitchToRoot(Root: string): Boolean; var Tp: UINT; begin Tp := GetDriveTp(Root); if Tp <> FDriveTp then case Tp of 0, 1: Exception.CreateFmt(SInvalidDrive, [Root]); DRIVE_FIXED: FFindInClass := TFindInHDD; else {DRIVE_REMOVABLE: DRIVE_REMOTE: DRIVE_CDROM: DRIVE_RAMDISK:} FFindInClass := TFindInRemovable; end; end; end. |