function TTestProgrammDlg.ReplaceInFile(
const FileName, SearchString, NewString: AnsiString;
CaseSensitive: Boolean): Longint;
{ returns position of string in file or -1, if not found }
const
BufferSize = $8001;
{ 32K+1 bytes }
var
pBuf, pEnd, pScan, pPos: PAnsiChar;
filesize: LongInt;
bytesRemaining: LongInt;
bytesToRead: Integer;
F:
file;
SearchFor: PAnsiChar;
oldMode: Word;
begin
Result := -1;
{ assume failure }
// NEW (first line): if length of string to search and new string does not match, cancel.
// Of course one could move the rest of the file accordingly instead.
if (Length(SearchString) <> Length(NewString))
or (Length(SearchString) = 0)
or (Length(FileName) = 0)
then Exit;
SearchFor :=
nil;
pBuf :=
nil;
{ open file as binary, 1 byte recordsize }
AssignFile(F, FileName);
oldMode := FileMode;
FileMode := fmOpenReadWrite;
{ NEW: access to read and write }
Reset(F, 1);
FileMode := oldMode;
try
{ allocate memory for buffer and pchar search string }
SearchFor := StrAlloc(Length(SearchString) + 1);
StrPCopy(SearchFor, SearchString);
if not caseSensitive
then { convert to upper case }
AnsiUpper(SearchFor);
GetMem(pBuf, BufferSize);
filesize := System.Filesize(F);
bytesRemaining := filesize;
pPos :=
nil;
while bytesRemaining > 0
do
begin
{ calc how many bytes to read this round }
if bytesRemaining >= BufferSize
then
bytesToRead := Pred(BufferSize)
else
bytesToRead := bytesRemaining;
{ read a buffer full and zero-terminate the buffer }
BlockRead(F, pBuf^, bytesToRead, bytesToRead);
pEnd := @pBuf[bytesToRead];
pEnd^ := #0;
{ scan the buffer. Problem: buffer may contain #0 chars! So we
treat it as a concatenation of zero-terminated strings. }
pScan := pBuf;
while pScan < pEnd
do
begin
if not caseSensitive
then { convert to upper case }
AnsiUpper(pScan);
pPos := StrPos(pScan, SearchFor);
{ search for substring }
if pPos <>
nil then
begin { Found it! }
Result := FileSize - bytesRemaining +
Longint(pPos) - Longint(pBuf);
// NEW: replace it
Seek(F, Result);
BlockWrite(F, PAnsiChar(NewString)^, Length(NewString));
Break;
end;
pScan := StrEnd(pScan);
Inc(pScan);
end;
if pPos <>
nil then Break;
bytesRemaining := bytesRemaining - bytesToRead;
if bytesRemaining > 0
then
begin
{ no luck in this buffers load. We need to handle the case of
the search string spanning two chunks of file now. We simply
go back a bit in the file and read from there, thus inspecting
some characters twice
}
Seek(F, FilePos(F) - Length(SearchString));
bytesRemaining := bytesRemaining + Length(SearchString);
end;
end;
{ While }
finally
CloseFile(F);
if SearchFor <>
nil then StrDispose(SearchFor);
if pBuf <>
nil then FreeMem(pBuf, BufferSize);
end;
end;
{ ScanFile }