Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Delphi: Общие вопросы > Сохранение записи сложной структры в файл


Автор: R@RED 17.10.08, 07:24
Здравстсвуйте!

Никак не получается сохранить в файл record, а затем вновь загрузить его! Запись имеет большой размер, состоит из полей разного типа, безразмерных массивов, переменных других типой(определенных) мной.

Пробовал очень просто, через поток:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    var
      fs: TFileStream;
    begin
      if sd.Execute then begin
        fs:=TFileStream.Create(sd.FileName,fmCreate);
        fs.WriteBuffer(Project, SizeOf(Project));
        fs.Free;
      end;
    end;

Ну и соответственно загрузка:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    var
      fs: TFileStream;
    begin
      if not od.Execute then exit;
     
      fs:=TFileStream.Create(od.FileName,fmOpenRead);
      fs.ReadBuffer(Project, fs.Size);
      fs.Free;
    end;

Ничего не получилось... (( Подскажите как это реализовать? Заранее спасибо!

Автор: Romkin 17.10.08, 07:35
Цитата R@RED @
безразмерных массивов,

Ну не безразмерных, конечно, а динамических :)
Поэтому и не получается. Самое простое - сделай не запись, а потомка TComponent. Все, что нужно сохранять, объяви как published property. И потом просто используй поток.

Автор: Domino 17.10.08, 08:24
Цитата R@RED @
Подскажите как это реализовать? Заранее спасибо!
Показывать надо не только краешек, ниже спускать, как у доктора, иначе тебе никто не поможет. Что за Project?

Автор: Демо 17.10.08, 09:35
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      TSampleRec=record
        Text: String;
        Num: Integer;
      end;


<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      i: integer;
      sr: TSampleRec;
      fs: TFileStream;
    begin
     
      sr.Text := 'test text';
      sr.Num := 19;
     
    //  Пишем
      fs := TFileStream.Create('c:\temp\file.tst', fmCreate);
      try
        i := Length(sr.Text);
        fs.Write(i,4);
        fs.Write(sr.Text[1],Length(sr.Text));
        fs.Write(sr.Num,4);
      finally
        fs.Free;
      end;
     
    //  Читаем
      fs := TFileStream.Create('c:\temp\file.tst', fmOpenRead);
      try
        fs.Read(i,4);
        SetLength(sr.Text,i);
        fs.Read(sr.Text[1],i);
        fs.Read(sr.Num,4);
      finally
        fs.Free;
      end;

Автор: Rouse_ 17.10.08, 09:38
Цитата Romkin @
Все, что нужно сохранять, объяви как published property. И потом просто используй поток.

Угу, а потом когда в след версии измениться логика чтения/сохранения RTTI свойств, попробуй всю эту байду прочитать :)

Автор: Marriage 17.10.08, 10:05
Если косяк именно из-за дин. массивов, то почему бы в структуре не хранить число элементов и написть свою процедуру считывания данных, а не потоком ?

Автор: --Ins-- 17.10.08, 11:12
Цитата Демо @
fs.Write(i,4);


Опять Демо новичков плохому учит! :D

Автор: Демо 17.10.08, 11:32
Цитата --Ins-- @
Опять Демо новичков плохому учит!


:D

Это почему это? -))

Автор: --Ins-- 17.10.08, 11:38
Цитата Демо @
Это почему это? -))


http://ru.wikipedia.org/wiki/Магическое_число_(программирование) - см. "Плохая практика программирования"
Про SizeOf слышал? ;)

Автор: Демо 17.10.08, 12:18
Цитата --Ins-- @
Про SizeOf слышал?


Увы, переносимости в любом случае не будет.
Как раз в этом случае integer не может менять длину. Оно ОБЯЗАНО быть 4 байта. Иначе нет смысла хранить данные таким образом.

Добавлено
PS.
Википедия - весьма спорный аргумент.
Я бы сказал, что ей доверять нельзя ВООБЩЕ.

Автор: --Ins-- 17.10.08, 12:21
Цитата Демо @
Увы, переносимости в любом случае не будет.


Уверен? А чем тогда отличаются фундаментальные типы от общих? А конкретно, Longint от Integer?

Добавлено
Цитата Демо @
Википедия - весьма спорный аргумент.


Согласен, но не в данном случае ;)

Автор: Демо 17.10.08, 12:37
Цитата --Ins-- @
Уверен? А чем тогда отличаются фундаментальные типы от общих? А конкретно, Longint от Integer?


Поможет ли SizeOf в случае переноса данных с одного ПК на другой, если на одном целый тип будет занимать 8 байт, а на другом 4?
Думаю, что нет. Поэтому в этом случае неприменим SizeOf.

Автор: --Ins-- 17.10.08, 12:38
Цитата Демо @
Поможет ли SizeOf в случае переноса данных с одного ПК на другой, если на одном целый тип будет занимать 8 байт, а на другом 4?
Думаю, что нет. Поэтому в этом случае неприменим SizeOf.


Тэкс, ты вопроса про общие и фундаментальные типы не понял... Что ж, спрошу без намеков... SizeOf(Longint) применим?

Автор: Демо 17.10.08, 12:44
Цитата --Ins-- @
Тэкс, ты вопроса про общие и фундаментальные типы не понял... Что ж, спрошу без намеков... SizeOf(Longint) применим?


Longint применим;)

Добавлено
В смысле Sizeof(Longint)...

Автор: --Ins-- 17.10.08, 12:47
Демо, ну дык чего ты мне доказываешь :) Видишь, и SizeOf применим, и переносимость, и нет "магических чисел" в коде

Автор: Демо 17.10.08, 12:55
Цитата --Ins-- @
Видишь, и SizeOf применим


Ты скажи мне, фундаментальный тип всегда останется неизменного размера?;) Уверен в том?

Автор: Rouse_ 17.10.08, 13:10
Write заменить на WriteBuffer а по поводу SizeOf есть вот такой вопросик:
SizeOf(Char) = ? ;)

Автор: R@RED 17.10.08, 14:26
Хых, страшно рад, что мой вопрос вызвал такой оживленный спор... =)
Цитата Rouse_ @
TSampleRec=record
Text: String;
Num: Integer;
end;


i: integer;
sr: TSampleRec;
fs: TFileStream;
begin

sr.Text := 'test text';
sr.Num := 19;

// Пишем
fs := TFileStream.Create('c:\temp\file.tst', fmCreate);
try
i := Length(sr.Text);
fs.Write(i,4);
fs.Write(sr.Text[1],Length(sr.Text));
fs.Write(sr.Num,4);
finally
fs.Free;
end;

// Читаем
fs := TFileStream.Create('c:\temp\file.tst', fmOpenRead);
try
fs.Read(i,4);
SetLength(sr.Text,i);
fs.Read(sr.Text[1],i);
fs.Read(sr.Num,4);
finally
fs.Free;
end;

Rouse, если бы все было так просто, я бы и вопрос не задавал! ) Такая мысль мне и самому приходила. Согласен, что так рекорд успешно запишется, но если учесть, что в нем несколько десятков полей, многие из которых также являются записями, которые также содержать большое количество полей и динамических массивов... К тому же размер файла существенно возрастет, если перед каждой переменной записывать ее размер, да и скорость будет не совсем приемлимой... Я уж не говорю о том, что я просто запарюсь печетать! =\
Может есть другой способ?

Автор: R@RED 17.10.08, 15:23
Ins! Rouse! Блин, ну создали бы что-ли отдельную ветку "Филосифия Delphi"! )) А у меня сейчас есть реальный вопрос и совсем не много времени! Плиз, ребята, помогите разобраться!

Автор: andrew.virus 17.10.08, 15:29
R@RED приведи пример твоей записи попробую помочь ...

Сообщения были разделены в тему "Фундаментальные и общие типы данных"

Автор: Virtuals 17.10.08, 16:35
хм нифигасе нафлудили, и кто?, люди облаченные властью и знаниями :P
а про то что помочь топикстартеру забыли? а проблема имхо на поверхности!
R@RED
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    type
    TProject =record
    num:word;
    doc:array[1..128] of char;
    end
    ..
    var Project:TProject;
    ...
    WriteBuffer(Project, SizeOf(Project));

сработает!
а вот
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    type
    TProject =record
    num:word;
    doc:Pchar;
    end
    ..
    var Project:TProject;
    ...
    WriteBuffer(Project, SizeOf(Project));

нет! а почему? догадался?

ЗЫ примерчик офтопа
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    как то раз пришел один ко мне на работе и говорит типа ты че лох говориш что песенки занимают примерно 1метр/минута, вот у меня на дискете 400 песен и еще место осталось. и как вы думаете что было на дискете? правильно... 400 ярлыков, у дома у него все работало


не используй указательные типы (pchar это указатель на на массив символов, имеет размер sizeof(pointer), а данные находятся в другой области памяти) 8-)

Автор: R@RED 18.10.08, 04:00
Цитата andrew.virus @
приведи пример твоей записи попробую помочь ...

Ок, по определенным причинам не могу привести конкретно свой рекорд, но приведу подобный, если удасться сохранить его, то и проблема будет решена:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    type TRecType1 = (rtRTV1, rtRTV2);
    type TRecType2 = set of (rttRTV1, rttRTV2);
     
    type TSubRec = record
        str1   : string;
        str2   : ShortString;
        int1   : integer;
        int2   : LongInt;
        _type  : TRecType1;
        _type1 : TRecType2;
        mass1  : array of string;
        mass2  : array of integer;
        bool1 : boolean;
    end;
     
    type TProject = record
        mass1 : array of TSubRec;
        str1  : string;
        int1  : LongInt;
        int2  : integer;
        bool1 : boolean;
    end;

Только необходимо учитывать, что полей и типов гораздо больше, так что способ Демо не подходит. Есть идеи? =)

Автор: Virtuals 18.10.08, 05:33
R@REDмой пост выше читал?
sizeof(mass1) =4 !!! как думаеш почему?
кстати string тоже указатель :D
фиксированный только ShortString

Добавлено
в твоем примере

type TRecType1 = (rtRTV1, rtRTV2);
type TRecType2 = set of (rttRTV1, rttRTV2);

type TSubRec = record
str1 : string;
str2 : ShortString;
int1 : integer;
int2 : LongInt;
_type : TRecType1;
_type1 : TRecType2;
mass1 : array of string;
mass2 : array of integer;
bool1 : boolean;
end;

type TProject = record
mass1 : array of TSubRec;
str1 : string;
int1 : LongInt;
int2 : integer;
bool1 : boolean;
end;
это ошибки

Автор: R@RED 18.10.08, 09:21
Цитата Virtuals @
sizeof(mass1) =4 !!! как думаеш почему?

Почему же?
Цитата Virtuals @
это ошибки

Что значит ошибки, если мне необходимо использование именно этих типов? Если string на какой то подобный строковой тип я еще смогу заменить, то как например я должен обходиться без динамических массивов?
Извини, если чего-то не понял, буду рад, если приведешь правильный пример записи.

Автор: Демо 18.10.08, 10:03
R@RED
Универсального решения для сохранения структур в файл нет.
Принцип понятен, остальное - кодирование.
Т.е. каждую структуру нужно обрабатывать по-своему.

Автор: R@RED 18.10.08, 10:08
Цитата Демо @
каждую структуру нужно обрабатывать по-своему.

Цитата R@RED @
Согласен, что так рекорд успешно запишется, но если учесть, что в нем несколько десятков полей, многие из которых также являются записями, которые также содержать большое количество полей и динамических массивов... К тому же размер файла существенно возрастет, если перед каждой переменной записывать ее размер, да и скорость будет не совсем приемлимой...

Я правильно понял, что в моем случае обрабатывать структуры по одтельности - это единственный путь и ни смотря на все минусы альтернативного решения нет? =(

Автор: Демо 18.10.08, 10:18
Цитата R@RED @
Я правильно понял, что в моем случае обрабатывать структуры по одтельности - это единственный путь и ни смотря на все минусы альтернативного решения нет? =(


Именно так.
А что, у тебя их так много?

Если структуры - своеобразные наследники друг друга, то альтернативный вариант - создание классов наследников, которые умеют писать себя в поток. Это поможет упростить код и умегьшить его объём.

Автор: R@RED 18.10.08, 10:22
Цитата Демо @
создание классов наследников, которые умеют писать себя в поток. Это поможет упростить код и умегьшить его объём.
Это уже интереснее... А как можно реализовать умение "писать себя в поток", если опять же не пробегать все элементы по отдельности?

Автор: Демо 18.10.08, 10:38
Цитата R@RED @
А как можно реализовать умение "писать себя в поток", если опять же не пробегать все элементы по отдельности?


Писать каждый элемент в любом случае придётся. Упрощение возможно лишь в случае наследования когда каждый наследник лишь добавляет новые поля для записи, а предок уже умеет работать с остальными полями.

Автор: R@RED 18.10.08, 10:40
Оуу... Ясно! Что ж, видимо все таки придеться писать все по очереди... =\ Ладно, спасибо за ответы, буду рад если у кого то еще появятся какие то идеи.

Автор: Virtuals 18.10.08, 11:33
R@RED
Цитата
Цитата (Virtuals @ Сегодня, 08:33)
sizeof(mass1) =4 !!! как думаеш почему?

Почему же?

потому что это указатель pointer (аналог ярлыка в виндах, типа написано на бумажке "машина стоит за углом") тоесть самих данных в переменной нет, она ссылается на область памяти где хранятся сами данные.

Цитата
то как например я должен обходиться без динамических массивов?

а как вы узнаете размер вашего массива? ;) чтоб не вызвать AV
если для строк еще боле менее узнать размер (ну напр по терминирующему #0)
то как узнать размер структуры
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    mass2 : array of integer;

?
в от с
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    mass2 : array of string;


вообще грабли, вот разберем этот пример

mass2 : array of string;
есть не что иное как
указатель на область памяти, где хранится
массив, произвольной длинны, указателей, которые ссылаются на
области памяти, произвольной длинны, где данные, теоритически, оканчиваются на #0

вот мы и пришли к тому что "сложные" структуры, (тоесть такие где используются переменные, с неизвестным размером") лучше сохранять в какую либо БД, а какая это БД будет вам решать, но учтите что в ней каждый раз должно сохранятся неизвестное количество полей с произвольной длинной

ЗЫ смотрите в сторону XML

Автор: R@RED 18.10.08, 11:39
Цитата Virtuals @
потому что это указатель pointer
Потом уже догадался )
Цитата Virtuals @
есть не что иное как указатель на область памяти, где хранится, массив, произвольной длинны, указателей, которые ссылаются на области памяти, произвольной длинны, где данные, теоритически, оканчиваются на #0
Ясненько! )
Цитата Virtuals @
вот мы и пришли к тому что "сложные" структуры, (тоесть такие где используются переменные, с неизвестным размером") лучше сохранять в какую либо БД
Не катит, не спрашивайте почему, просто БД в этом случае мне не подходит...
Ладно, я уже почти смирился со способом предложенным Демо. )) Всем спасибо, вопрос решен.

Автор: Демо 18.10.08, 11:40
R@RED

Есть один вариант. Но насколько он сложен(или прост) не скажу, пока не попробую реализовать сам.
Подумаю немного-)

Автор: R@RED 18.10.08, 11:42
Буду очень рад свежей идее! =)

Автор: Romkin 18.10.08, 14:36
Я тебе уже дал идею в #2. То, что говорит Rouse по этому поводу - фигня. Но если уж хочется подстраховаться, то можно писать файл в текстовом виде, там уж разночтений не будет.

Автор: Демо 18.10.08, 17:31
Пример заготовки для копирования в TStream любой структуры:
Необходимо описание самой структуры (packed record) и строка с описанием типов полей: один символ - одно поле в структуре.
Это лишь заготовка, необходимо тестировать в различных условиях.

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    unit uTypes;
     
    interface
     
    uses
      Classes;
     
    type
     
      tRec=packed record
        fString: String;
        fInt: Integer;
        fInt64: Int64;
        fBoolean: Boolean;
      end;
     
    function GetRec(PointerToRecord: Pointer;
      RecordSH: String; Stream: TStream): Integer;
    function PutRec(PointerToRecord: Pointer;
      RecordSH: String; Stream: TStream): Integer;
     
    const
      RecSh='si6b';
     
    implementation
     
    function GetRec(PointerToRecord: Pointer;
      RecordSH: String; Stream: TStream): Integer;
    var
      p: Pointer;
      c: Char;
      i: integer;
      Len: Integer;
    begin
      Result := 0;
      p := PointerToRecord;
      for i := 1 to Length(RecordSH) do
      begin
        c := UpCase(RecordSH[i]);
        case c of
          'S':
            begin
              Stream.ReadBuffer(Len,SizeOf(Integer));
              SetLength(PString(p)^,Len);
              Stream.ReadBuffer(PString(p)^[1],Len);
              p := Pointer(Integer(p)+SizeOf(String));
              Result := Result + SizeOf(String)+Len;
            end;
          'I':
            begin
              Stream.ReadBuffer(PInteger(p)^,SizeOf(Integer));
              p := Pointer(Integer(p)+SizeOf(Integer));
              Result := Result + SizeOf(Integer);
            end;
          '6':
            begin
              Stream.ReadBuffer(PInt64(p)^,SizeOf(Int64));
              p := Pointer(Integer(p)+SizeOf(Int64));
              Result := Result + SizeOf(Int64);
            end;
          'B':
            begin
              Stream.ReadBuffer(PBoolean(p)^,SizeOf(Boolean));
              p := Pointer(Integer(p)+SizeOf(Boolean));
              Result := Result + SizeOf(Boolean);
            end;
        end;
      end;
    end;
     
    function PutRec(PointerToRecord: Pointer;
      RecordSH: String; Stream: TStream): Integer;
    var
      p: Pointer;
      c: Char;
      i: integer;
      Len: Integer;
    begin
      Result := 0;
      p := PointerToRecord;
      for i := 1 to Length(RecordSH) do
      begin
        c := UpCase(RecordSH[i]);
        case c of
          'S':
            begin
              Len := Length(PString(p)^);
              Stream.WriteBuffer(Len,SizeOf(Integer));
              Stream.WriteBuffer(PString(p)^[1],Len);
              p := Pointer(Integer(p)+SizeOf(String));
              Result := Result + SizeOf(String)+Len;
            end;
          'I':
            begin
              Stream.WriteBuffer(PInteger(p)^,SizeOf(Integer));
              p := Pointer(Integer(p)+SizeOf(Integer));
              Result := Result + SizeOf(Integer);
            end;
          '6':
            begin
              Stream.WriteBuffer(PInt64(p)^,SizeOf(Int64));
              p := Pointer(Integer(p)+SizeOf(Int64));
              Result := Result + SizeOf(Int64);
            end;
          'B':
            begin
              Stream.WriteBuffer(PBoolean(p)^,SizeOf(Boolean));
              p := Pointer(Integer(p)+SizeOf(Boolean));
              Result := Result + SizeOf(Boolean);
            end;
        end;
      end;
    end;
     
    end.


Пример использования:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    procedure TForm1.Button1Click(Sender: TObject);
    type
      pRec=^TRec;
    var
      fs: TFileStream;
      tr: PRec;
      tr1: TRec;
    begin
      New(tr);
      tr^.fString := 'ПРоверка записи строки в файл';
      tr^.fInt := 1;
      tr^.fInt64 := 2;
      tr^.fBoolean := True;
      fs := TFileStream.Create('c:\test.dat', fmCreate);
      try
        uTypes.PutRec(tr,RecSH,fs);
      finally
        fs.Free;
        Dispose(tr);
      end;
     
      fs := TFileStream.Create('c:\test.dat', fmOpenRead);
      try
        uTypes.GetRec(@tr1,RecSH,fs);
      finally
        fs.Free;
      end;
      ShowMessage(tr1.fString+'/'+
        IntToStr(tr1.fInt)+'/'+
        IntToStr(tr1.fInt64)+'/'+
        IntToStr(Integer(tr1.fBoolean)));
    end;

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)