сохранение записей, содержащих член типа string, в файл (Delphi, Windows)

StackOverflow https://stackoverflow.com/questions/1472325

Вопрос

У меня есть запись, которая выглядит примерно так:

type
  TNote = record
    Title : string;
    Note  : string;
    Index : integer;
  end;

Простой.Причина, по которой я решил установить переменные в виде строки (а не массива символов), заключается в том, что я понятия не имею, какой длины будут эти строки.Они могут иметь длину 1 символ, 200 или 2000.Конечно, когда я пытаюсь сохранить запись в файл типа (файл...), компилятор жалуется, что мне нужно указать размер строки.Есть ли способ преодолеть это?или способ сохранить эти записи в нетипизированный файл и при этом сохранить своего рода возможность поиска?

Пожалуйста, не указывайте мне на возможные решения, если вы знаете решение, пришлите код.Спасибо

Это было полезно?

Решение

Вы не можете сделать это с типизированным файлом.Попробуйте что-то вроде этого с TFileStream:

type
   TStreamEx = class helper for TStream
   public
      procedure writeString(const data: string);
      function readString: string;
      procedure writeInt(data: integer);
      function readInt: integer;
  end;

function TStreamEx.readString: string;
var
   len: integer;
   iString: UTF8String;
begin
   self.readBuffer(len, 4);
   if len > 0 then
   begin
      setLength(iString, len);
      self.ReadBuffer(iString[1], len);
      result := string(iString);
   end;
end;

procedure TStreamEx.writeString(const data: string);
var
   len: cardinal;
   oString: UTF8String;
begin
   oString := UTF8String(data);
   len := length(oString);
   self.WriteBuffer(len, 4);
   if len > 0 then
      self.WriteBuffer(oString[1], len);
end;

function TStreamEx.readInt: integer;
begin
   self.readBuffer(result, 4);
end;

procedure TStreamEx.writeInt(data: integer);
begin
   self.WriteBuffer(data, 4);
end;

type
  TNote = record
    Title : string;
    Note  : string;
    Index : integer;
    procedure Save(stream: TStream);
  end;

procedure TNote.Save(stream: TStream);
var
   temp: TMemoryStream;
begin
   temp := TMemoryStream.Create;
   try
      temp.writeString(Title);
      temp.writeString(Note);
      temp.writeInt(Index);
      temp.seek(0, soFromBeginning);
      stream.writeInt(temp.size);
      stream.copyFrom(temp, temp.size);
   finally
      temp.Free;
   end;
end;

Я оставлю процедуру загрузки вам.Та же основная идея, но временный поток не нужен.Благодаря размеру записи перед каждой записью вы можете прочитать ее и узнать, как далеко нужно пропустить, если вы ищете определенную запись # вместо того, чтобы читать ее целиком.

РЕДАКТИРОВАТЬ:Это было написано специально для версий Delphi, использующих строки Unicode.В более старых версиях это можно было немного упростить.

Другие советы

Почему бы не записать это в формате XML?Смотрите мою сессию»Практический XML с Delphi"о том, как с этим начать.

Другая возможность — превратить ваши записи в классы, убывающие от TComponent, и хранить/извлекать ваши данные в файлах DFM.

Эта запись Stackoverflow показывает, как это сделать.

--джероен

ПС:Извините, мой ответ XML был немного запутанным;Вообще-то я еду на две конференции (БАСТА! и ДельфиЖиви!Германия).

По сути, то, что вам нужно сделать, очень просто:создайте образец XML-файла, затем запустите Мастер привязки XML-данных Delphi (доступно в Delphi начиная с версии 6).

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

По приведенной выше ссылке представлено видео, демонстрирующее использование мастера привязки XML-данных Delphi.

Вы можете работать с двумя разными файлами: один просто хранит строки каким-либо удобным способом, а другой хранит записи со ссылкой на строки.Таким образом, у вас по-прежнему будет файл записей для быстрого доступа, даже если вы не знаете размер фактического содержимого.

(К сожалению, кода нет.)

  TNote = record
    Title : string;
    Note  : string;
    Index : integer;
  end;

можно перевести как

  TNote = record
    Title : string[255];
    Note  : string[255];
    Index : integer;
  end;

и используйте Stream.writebuffer(ANodeVariable, sizeof(TNode), но вы сказали, что в этом случае строки превышают 255 символов, ЕСЛИ строка превышает 65535 символов, тогда измените WORD на INTEGER

type
TNodeHeader=Record
  TitleLen,
  NoteLen: Word;
end;

(* this is for writing a TNode *)
procedure saveNodetoStream(theNode: TNode; AStream: TStream);
var
  header: TNodeHeader;
  pStr: PChar;
begin
  ...
  (* writing to AStream which should be initialized before this *)
  Header.TitleLen := Length(theNode.Title);
  header.NodeLen := Length(theNode.Note);
  AStream.WriteBuffer(Header, sizeof(TNodeHeader);
  (* save strings *)
  PStr := PChar(theNode.Title);
  AStream.writeBuffer(PStr^, Header.TitleLen);
  PStr := PChar(theNode.Note);
  AStream.writebuffer(PStr^, Header.NoteLen);
  (* save index *)
  AStream.writebuffer(theNode.Index, sizeof(Integer));
end;
(* this is for reading a TNode *)
function readNode(AStream: TStream): TNode;
var
  header: THeader
  PStr: PChar;
begin
  AStream.ReadBuffer(Header, sizeof(TNodeHeader);
  SetLength(Result.Title, Header.TitleLen);
  PStr := PChar(Result.Title);
  AStream.ReadBuffer(PStr^, Header.TitleLen);
  SetLength(Result.Note, Header.NoteLen);
  PStr := PChar(Result.Note);
  AStream.ReadBuffer(PStr^, Header.NoteLen);
  AStream.ReadBuffer(REsult.Index, sizeof(Integer)(* 4 bytes *);
end;

Вы можете использовать функции доступен в этом модуле с открытым исходным кодом.

Он позволяет сериализовать любое содержимое записи в двоичный формат, включая даже динамические массивы внутри:

type
  TNote = record
    Title : string;
    Note  : string;
    Index : integer;
  end;

var
  aSave: TRawByteString;
  aNote, aNew: TNote;
begin
  // create some content
  aNote.Title := 'Title';
  aNote.Note := 'Note';
  aNote.Index := 10;
  // serialize the content
  aSave := RecordSave(aNote,TypeInfo(TNote));
  // unserialize the content
  RecordLoad(aNew,pointer(aSave),TypeInfo(TNote));
  // check the content
  assert(aNew.Title = 'Title');
  assert(aNew.Note = 'Note');
  assert(aNew.Index = 10);
end;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top