Как я могу эффективно прочитать первые несколько строк многих файлов в Delphi - PullRequest
6 голосов
/ 30 января 2011

В моей программе есть функция «Найти файлы», которая находит текстовые файлы с суффиксом .ged, которые читает моя программа.Я отображаю найденные результаты в похожем на проводник окне, которое выглядит следующим образом:

enter image description here

Я использую стандартные методы FindFirst / FindNext, и это работает очень быстро.584 файла, показанные выше, найдены и отображены в течение нескольких секунд.

Теперь я хотел бы добавить на экран два столбца, которые показывают «Источник» и «Версия», содержащиеся вкаждый из этих файлов.Эта информация обычно находится в первых 10 строках каждого файла, в строках, которые выглядят следующим образом:

1 SOUR FTM
2 VERS Family Tree Maker (20.0.0.368)

Теперь у меня нет проблем с самоанализом, и это не то, о чем я спрашиваю.

Мне нужна помощь в том, чтобы просто как можно быстрее загрузить первые 10 или около того строк из этих файлов, чтобы я мог их проанализировать.но загрузка больших файлов занимает слишком много времени, например, при объеме более 1 МБ.

Поскольку мне нужны только первые 10 строк или около того, как мне их лучше всего получить?

Я использую Delphi 2009, и мои входные файлы могут быть или не быть Unicode, так что этодолжен работать для любой кодировки.


Продолжение: Спасибо Антонио,

Я закончил тем, что сделал это, что отлично работает:

var
  CurFileStream: TStream;
  Buffer: TBytes;
  Value: string;
  Encoding: TEncoding;

try
  CurFileStream := TFileStream.Create(folder + FileName, fmOpenRead);
  SetLength(Buffer, 256);
  CurFileStream.Read(Buffer[0], 256);
  TEncoding.GetBufferEncoding(Buffer, Encoding);
  Value := Encoding.GetString(Buffer);
  ...
  (parse through Value to get what I want)
  ...
finally
  CurFileStream.Free;
end;

Ответы [ 5 ]

14 голосов
/ 30 января 2011

Используйте TFileStream и с методом Read считайте необходимое количество байтов. Вот пример чтения информации о растровом изображении, которая также сохраняется в начале файла.

http://www.delphidabbler.com/tips/19

4 голосов
/ 30 января 2011

Просто откройте файл для чтения блоков (не используя встроенную функциональность TStringList) и прочитайте первый блок файла, а затем вы можете, например, загрузить этот блок в список строк с помощью strings.SetText () (если выиспользуя функции блока) или просто strings.LoadFromStream (), если вы загружаете свои блоки с помощью потоков.

Я бы лично пошел с функциями блока FileRead / FileWrite и загрузил блок в буфер.Вы также можете использовать функции similair winapi, но это просто больше кода без всякой причины.

ОС читает файлы в блоках, которые имеют размер не менее 512 байт практически на любой платформе / файловой системе, поэтому вы можете сначала прочитать 512 байти надеемся, что вы получили все 10 строк, что будет правдой, если ваши строки будут достаточно короткими).Это будет (практически) так же быстро, как чтение 100 или 200 байтов.

Тогда, если вы заметите, что ваши строковые объекты имеют менее 10 строк, просто прочитайте следующий 512-байтовый блок и попробуйте снова проанализировать.(Или просто используйте блоки 1024, 2048 и т. Д., Во многих системах это, вероятно, будет так же быстро, как 512 блоков, так как размеры кластеров файловой системы обычно больше, чем 512 байт).

PS.Кроме того, используя потоки или асинхронные функции в функциях файлов Winapi (CreateFile и т. Д.), Вы можете загружать эти данные из файлов асинхронно, в то время как остальная часть вашего приложения работает.В частности, интерфейс не будет зависать при чтении больших каталогов.

Это сделает загрузку вашей информации более быстрой (так как список файлов будет загружен напрямую, а через несколько миллисекунд появится остальная информация), в то же время фактически не увеличивая реальную скорость чтения.

Делайте это только в том случае, если вы попробовали другие методы и чувствуете, что вам нужен дополнительный импульс.

3 голосов
/ 01 февраля 2011

Вы можете использовать TStreamReader для чтения отдельных строк из любого TStream объекта, такого как TFileStream. Для еще более быстрого файлового ввода-вывода вы можете использовать Memory-Mapped Views с TCustomMemoryStream.

2 голосов
/ 01 февраля 2011

Хорошо, я удалил свой первый ответ. Используя первое предложение Реми, я попробовал еще раз со встроенными вещами. Что мне здесь не нравится, так это то, что вы должны создать и освободить два объекта. Я думаю, я бы сделал свой собственный класс, чтобы обернуть это:

var
  fs:TFileStream;
  tr:TTextReader;
  filename:String;
begin
  filename :=  'c:\temp\textFileUtf8.txt';
  fs := TFileStream.Create(filename, fmOpenRead);
  tr := TStreamReader.Create(fs);
  try
      Memo1.Lines.Add( tr.ReadLine );

  finally
    tr.Free;
    fs.Free;
  end;   
end;

Если кому-то интересно то, что у меня было здесь раньше, у него возникла проблема не работать с файлами Юникода.

0 голосов
/ 31 января 2011

Иногда старшкольный паскаль не так уж и плох.Несмотря на то, что не-oo доступ к файлам, кажется, больше не очень популярен, ReadLn(F,xxx) все еще работает довольно хорошо в ситуациях, подобных вашей.

Код ниже загружает информацию (имя файла, источник и версию) в TDictionary, чтобы вы могли легко найти его, или вы можете использовать просмотр списка в виртуальном режиме, и искать вещи в этом списке, когда даже срабатывает ondata.

Предупреждение: приведенный ниже код не работает сUnicode.

program Project101;
{$APPTYPE CONSOLE}

uses
  IoUtils, Generics.Collections, SysUtils;

type
  TFileInfo=record
    FileName,
    Source,
    Version:String;
  end;

function LoadFileInfo(var aFileInfo:TFileInfo):Boolean;
var
  F:TextFile;
begin
  Result := False;
  AssignFile(F,aFileInfo.FileName);
  {$I-}
  Reset(F);
  {$I+}
  if IOResult = 0 then
  begin
    ReadLn(F,aFileInfo.Source);
    ReadLn(F,aFileInfo.Version);
    CloseFile(F);
    Exit(True)
  end
  else
    WriteLn('Could not open ', aFileInfo.FileName);
end;

var
  FileInfo:TFileInfo;
  Files:TDictionary<string,TFileInfo>;
  S:String;
begin
  Files := TDictionary<string,TFileInfo>.Create;
  try
    for S in TDirectory.GetFiles('h:\WINDOWS\system32','*.xml') do
    begin
      WriteLn(S);
      FileInfo.FileName := S;
      if LoadFileInfo(FileInfo) then
        Files.Add(S,FileInfo);
    end;

    // showing file information...
    for FileInfo in Files.Values do
      WriteLn(FileInfo.Source, ' ',FileInfo.Version);
  finally
    Files.Free
  end;
  WriteLn;
  WriteLn('Done. Press any key to quit . . .');
  ReadLn;
end.
...