function ScanFile(const FileName: string;
const forString: string;
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: PChar;
filesize: LongInt;
bytesRemaining: LongInt;
bytesToRead: Word;
F: file;
SearchFor: PChar;
oldMode: Word;
begin
Result := -1; { assume failure }if (Length(forString) = 0) or (Length(FileName) = 0) then Exit;
SearchFor := nil;
pBuf := nil;
{ open file as binary, 1 byte recordsize }
AssignFile(F, FileName);
oldMode := FileMode;
FileMode := 0; { read-only access }
Reset(F, 1);
FileMode := oldMode;
try{ allocate memory for buffer and pchar search string }
SearchFor := StrAlloc(Length(forString) + 1);
StrPCopy(SearchFor, forString);
ifnot caseSensitive then{ convert to upper case }
AnsiUpper(SearchFor);
GetMem(pBuf, BufferSize);
filesize := System.Filesize(F);
bytesRemaining := filesize;
pPos := nil;
while bytesRemaining > 0 dobegin{ 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 dobeginifnot caseSensitive then{ convert to upper case }
AnsiUpper(pScan);
pPos := StrPos(pScan, SearchFor); { search for substring }if pPos <> nilthenbegin{ Found it! }
Result := FileSize - bytesRemaining +
Longint(pPos) - Longint(pBuf);
Break;
end;
pScan := StrEnd(pScan);
Inc(pScan);
end;
if pPos <> nilthen Break;
bytesRemaining := bytesRemaining - bytesToRead;
if bytesRemaining > 0 thenbegin{ 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(forString));
bytesRemaining := bytesRemaining + Length(forString);
end;
end; { While }finally
CloseFile(F);
if SearchFor <> nilthen StrDispose(SearchFor);
if pBuf <> nilthen FreeMem(pBuf, BufferSize);
end;
end; { ScanFile }
Перевод контента на русский язык:
Функция Delphi ScanFile сканирует файл для указанной строки и возвращает позицию первого вхождения строки в файле. Если строка не найдена, функция возвращает -1.
Распределение работы функции:
Функция принимает три параметра: FileName, forString и caseSensitive. FileName - имя файла для сканирования, forString - строка для поиска, а caseSensitive - булевое значение, указывающее, является ли поиск чувствительным к регистру или нет.
Функция проверяет, не пусты ли FileName или forString. Если это так, функция возвращает сразу без выполнения дальнейших операций.
Затем функция открывает файл, указанный в FileName, в бинарном режиме и сбрасывает его позицию к началу файла.
Функция выделяет память для буфера (называемого pBuf) и строки поиска (называемой SearchFor). Размер буфера установлен в 32K+1 байт, что является разумным размером для большинства файлов.
Функция затем вступает в цикл, который читает файл блоками до 32K байт в каждом. Для каждого блока функция ищет указанную строку с помощью функции StrPos и обновляет свою позицию соответственно.
Если строка найдена, функция возвращает позицию первого вхождения строки в файле.
Если цикл завершается без нахождения строки, функция возвращает -1.
Некоторые потенциальные проблемы с этим кодом:
Поиск строки может быть неэффективным, если файл содержит много вхождений одной и той же строки, поскольку функция будет продолжать поиск откуда она оставила, даже после обнаружения совпадения.
Код предполагает, что файл мал, чтобы поместиться в память. Если файл очень большой, это может вызвать проблемы с памятью.
Альтернативное решение могло бы быть использованием подхода потокового чтения, где функция читает файл по одному байту за раз и ищет строку на ходу. Это бы устранило необходимость в буферах и уменьшило потребление памяти. Однако, это подход может быть медленнее, чем текущая реализация.
Пример того, как функция могла быть переписана с использованием потокового подхода:
Эта реализация может быть более память-эффективной, чем оригинальный код, но может быть медленнее из-за дополнительного чтения по одному байту за раз.
Сканируем файл в поисках текста: функция ScanFile позволяет найти положение указанного текстового fragments в файле.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.