Re: Eindeutiger Vergleich für große Dateien gesucht
3. Aug 2005, 18:16
ok, hier ist der vergleich. ich weise aber darauf hin, dass die prozedur CompareFilesFileStream die daten von vorne nach hinten liest, während CompareFilesMemoryMapped von hinten nach vorne (s. negaH's post). das heisst die prozedur CompareFilesFileStream müsste dahingehend optimiert werden um einen einigermaßen adequaten vergleich zu erzielen.
zum vergleich:
daten die überprüft wurden: 22 dateien, gesamtgröße: 6.480.244.455 Bytes. dabei war die datei, auf die es ankommt (wwdofficialnltls7l1.exe, 1.438.201.380 Bytes) zweifach vorhanden, um zu testen ob sie als unterschiedlich oder gleich erkannt wird.
function CompareFilesFileStream(const File1, File2: String): Boolean; const
BlockSize = 65536; var
FSFile1, FSFile2: TFileStream;
L1, L2: Integer;
B1, B2: Array[1..BlockSize] of Byte; begin
Result := False;
FSFile1 := TFileStream.Create(File1, fmOpenRead or fmShareDenyWrite); try
FSFile2 := TFileStream.Create(File2, fmOpenRead or fmShareDenyWrite); try if FSFile1.Size = FSFile2.Size then begin while FSFile1.Position < FSFile1.Size do begin
L1 := FSFile1.Read(B1[1], BlockSize);
L2 := FSFile2.Read(B2[1], BlockSize); if L1 <> L2 then
Exit; ifnot CompareMem(@B1[1], @B2[1], L1) then
Exit; end;
Result := True; end; finally
FSFile2.Free; end; finally
FSFile1.Free; end; end;
function GetHugeFileSize(const Filename: String): Int64; var
hFile: Longword;
Data: WIN32_FIND_DATA;
Size: LARGE_INTEGER; begin
Result := -1;
hFile := FindFirstFile(PChar(Filename), Data); try if hFile <> INVALID_HANDLE_VALUE then begin
Size.LowPart := Data.nFileSizeLow;
Size.HighPart := Data.nFileSizeHigh;
Result := Size.QuadPart; end; finally
Windows.FindClose(hFile); end; end;
function GetSystemAllocationGranularity: Cardinal; var
PSysInfo: TSystemInfo; begin
GetSystemInfo(PSysInfo);
Result := PSysInfo.dwAllocationGranularity; end;
function CompareFilesMemoryMapped(const File1, File2: String; SysAllocSize: Cardinal): Boolean; var
CurSize: DWord;
CurPos: Int64;
hFile1, hFile2: THandle;
hMap1, hMap2: THandle;
FileSize: Int64;
P1, P2: Pointer; begin
Result := False;
if hFile1 <> INVALID_HANDLE_VALUE then try
hFile2 := CreateFile(@File2[1], GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if hFile2 <> INVALID_HANDLE_VALUE then try
FileSize := GetHugeFileSize(File1); //GetFileSizeInt64(hFile1); if FileSize = GetHugeFileSize(File2) then//GetFileSizeInt64(hFile1) then begin
Result := True; if FileSize > 0 then begin
hMap1 := CreateFileMapping(hFile1, nil, PAGE_READONLY, 0, 0, nil); if hMap1 <> INVALID_HANDLE_VALUE then try
hMap2 := CreateFileMapping(hFile2, nil, PAGE_READONLY, 0, 0, nil); if hMap2 <> INVALID_HANDLE_VALUE then try
CurSize := FileSize mod SysAllocSize; if CurSize = 0 then
CurSize := SysAllocSize;
CurPos := FileSize - CurSize; repeat
P1 := MapViewOfFile(hMap1, FILE_MAP_READ, Int64Rec(CurPos).Hi, Int64Rec(CurPos).Lo, CurSize); if P1 <> nilthen try
P2 := MapViewOfFile(hMap2, FILE_MAP_READ, Int64Rec(CurPos).Hi, Int64Rec(CurPos).Lo, CurSize); if P2 <> nilthen try
Result := CompareMem(P1, P2, CurSize); finally
UnmapViewOfFile(P2); end; // else RaiseLastWin32Error; finally
UnmapViewOfFile(P1); end; // else RaiseLastWin32Error;
CurPos := CurPos - CurSize;
CurSize := SysAllocSize; until
(CurPos <= 0) or (not Result); finally
CloseHandle(hMap2); end; // else RaiseLastWin32Error; finally
CloseHandle(hMap1); end; // else RaiseLastWin32Error; end; end; finally
CloseHandle(hFile2); end; // else RaiseLastWin32Error; finally
CloseHandle(hFile1); end; // else RaiseLastWin32Error; end;
ich hoffe, dass soweit alles fehlerfrei ist. am besten wäre natürlich, wenn du beide prozeduren selbst mal testest und mir eventuelle fehler nennst.
hinweis: ich habe während der beiden vergleiche noch andere programme/dienste laufen usw. d. h. die vergleiche sind nicht sonderlich aussagekräftig, aber ich denke das ergebnis spricht für sich (wie gesagt, falls alles richtig ist).
das maßgebliche an den screenshots ist die zeit (statusbar rechts unten).
Re: Eindeutiger Vergleich für große Dateien gesucht
3. Aug 2005, 18:42
wie gesagt, es können noch fehler enthalten sein.
ich habe mal das boolean statement durch integer ersetzt und bekomme zwar auch je nach datei mal eine -1 zurück, aber soweit ich das sehe nur deshalb, weil die dateien ungleich sind. doppelte (gleiche/identische) dateien werden richtig bestimmt.
ich gebe dir recht, die benötigte zeit ist absolut erstaunlich, aber selbst mit breakpoints scheint er zumindest bei fragwürdigen dateien alles korrekt auszuführen.
wo könnte der fehler sein?
edit: jetzt habe ich mal negaH's RaiseLastWin32Error wieder aktiviert.
ich bekomme einen "System Error Code 1132: Die angegebene Basisadresse oder Dateioffset ist falsch ausgerichtet."
werde mal überprüfen wie und warum. danke für den hinweis!
Re: Eindeutiger Vergleich für große Dateien gesucht
3. Aug 2005, 18:51
Ziehe mal eine weitere Kopie der doppelten Datei und ändere in dieser Datei möglichst nur 1 Byte exakt in der Mitte der Datei.
Auch ich meine das 0.2 Sekunden enorm schnell ist, einfach zu schnell.
Das Result auf Integer zu setzen ist keine gute Idee und lass doch mal alle RaiseLastWin32Error drinnen. Falls nämlich irgendeine API Funktion fehlschlägt so wird eine Exception auftreten und schwups finden wir den eventuellen Fehler.
For the lpvBase parameter specified in a call to MapViewOfFileEx(), you should use an integral multiple of the system's allocation granularity. On Windows NT, not specifying such a value will cause MapViewOfFileEx() to fail, and GetLastError() to return ERROR_MAPPED_ALIGNMENT (1132). On Windows 95, the address is rounded down to the nearest integral multiple of the system's allocation granularity.
Re: Eindeutiger Vergleich für große Dateien gesucht
3. Aug 2005, 19:23
ich würde dich auch noch bitten (wenn du die fehler korrigiert hast) auch noch meine methode zu testen, da ich damit mit größeren blöcken arbeite und mach meiner theorie bei großen dateien, dort mehr speed rauskommen KÖNNTE
function SameFile(File1,File2:String):boolean; var
s1,s2:Tfilestream;
block:int64;
buffer1,buffer2: string; begin
result := false; try
s1 := Tfilestream.Create(File1,fmOpenRead);
s2 := Tfilestream.Create(File2,fmOpenRead);
if s1.Size <> s2.Size thenbegin
result := false; endelsebegin
s1.Position := 0;
s2.Position := 0;
block := 10485760; //10MB hier eventuell mal verschiedene werte ausprobieren (1,10,20,30,100) if s1.Position+block > s1.Size-1 then
block := s1.Size-s1.Position;
result := true;
setlength(buffer1,block);
setlength(buffer2,block); while( s1.Position <> s1.Size ) dobegin if s1.Position+block > s1.Size-1 then
block := s1.Size-s1.Position;
s1.Read(buffer1[1],block);
s2.Read(buffer2[1],block); if buffer1 <> buffer2 thenbegin
result := false;
break; end;
end;
end; finally
s1.Free;
s2.Free; end; end;
Das echte Leben ist was für Leute...
... die im Internet keine Freunde finden!