2006. május 1., hétfő
Compare two files and find the differences
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.
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése