Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум на Исходниках.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 |
Ну не безразмерных, конечно, а динамических Поэтому и не получается. Самое простое - сделай не запись, а потомка TComponent. Все, что нужно сохранять, объяви как published property. И потом просто используй поток. |
Автор: Domino 17.10.08, 08:24 |
Показывать надо не только краешек, ниже спускать, как у доктора, иначе тебе никто не поможет. Что за 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 |
Опять Демо новичков плохому учит! |
Автор: Демо 17.10.08, 11:32 |
Это почему это? -)) |
Автор: --Ins-- 17.10.08, 11:38 |
http://ru.wikipedia.org/wiki/Магическое_число_(программирование) - см. "Плохая практика программирования" Про SizeOf слышал? |
Автор: Демо 17.10.08, 12:18 |
Увы, переносимости в любом случае не будет. Как раз в этом случае 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 |
Ты скажи мне, фундаментальный тип всегда останется неизменного размера? Уверен в том? |
Автор: 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 |
хм нифигасе нафлудили, и кто?, люди облаченные властью и знаниями а про то что помочь топикстартеру забыли? а проблема имхо на поверхности! 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), а данные находятся в другой области памяти) |
Автор: R@RED 18.10.08, 04:00 |
Ок, по определенным причинам не могу привести конкретно свой рекорд, но приведу подобный, если удасться сохранить его, то и проблема будет решена: <{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 тоже указатель фиксированный только 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 |
Почему же? Что значит ошибки, если мне необходимо использование именно этих типов? Если 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 @ Ясненько! )есть не что иное как указатель на область памяти, где хранится, массив, произвольной длинны, указателей, которые ссылаются на области памяти, произвольной длинны, где данные, теоритически, оканчиваются на #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; |