Сохранить TList в поток TMemoryStream и восстановить обратно

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Ответить
Аватара пользователя
Nik
энтузиаст
Сообщения: 573
Зарегистрирован: 03.02.2006 23:08:09
Откуда: Киров
Контактная информация:

Сохранить TList в поток TMemoryStream и восстановить обратно

Сообщение Nik »

Продублирую вопрос из этой ветки в общем разделе.

Требуется сохранить массив указателей TList в поток TMemoryStream. Указатели разного типа (строки, int, doube), а потом передать на другой комп (это не проблема) и восстановить точную копию исходного TList.

Я пробовал в лоб:

Код: Выделить всё

fResults: TList;
...
res: TMemoryStream;
...

res.Write(fResults[i]^, SizeOf(fResults[i]));


Но при таком раскладе в потоке оказывается ерунда (что предсказуемо). Если писать fResults[i] - в поток попадают (если я правильно понял) адреса.

Кто-нибудь знает, как корректно записать Pointer неизвестного типа в поток и потом их обратно считать? Возможно ли это в принципе силами FPC/Lazarus?
zub
долгожитель
Сообщения: 2889
Зарегистрирован: 14.11.2005 22:51:26
Контактная информация:

Сообщение zub »

В смысле записать и восстановить указатель? указатель он на то и указатель, что уже в следующей сессии будет пальцем в небо, не говоря уже о x32\x64

Код: Выделить всё

res.Write(fResults[i]^, SizeOf(fResults[i]));

пишете первые 4\8 байт от того на что ссылается fResults[i]^
Если указатель ссылается на чтото неизвестного типа - это никак не сохранить (хотя если вы с этим работаете, значит тип знаете), тип надо знать и писать вместе с содержимым, чтоб потом однозначно восстановить
Аватара пользователя
Nik
энтузиаст
Сообщения: 573
Зарегистрирован: 03.02.2006 23:08:09
Откуда: Киров
Контактная информация:

Сообщение Nik »

Я пытаюсь сохранить данные по указателю (fResults[i]^). Тип, теоретически, я знаю (их всего 4, все вполне конкретные - строки, числа). Но как определить тип конкретного указателя - понять пока не могу (в массиве они перемешаны, всё зависит от типа данных в базе данных, которая лежит в конкретном TList).
Max Rusov
постоялец
Сообщения: 191
Зарегистрирован: 25.04.2009 15:46:03

Сообщение Max Rusov »

Если Вы не знаете тип - задача не имеет смысла. Такие данные не только в поток нельзя записать, Вы их и локально никак не сможете обработать. Т.ч. - ищите доступ к типам.
Аватара пользователя
hinst
энтузиаст
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Сообщение hinst »

Но как определить тип конкретного указателя - понять пока не могу

нельзя! более того, вы не только на другой комп не сможете их перекачать, но и на своём компе использовать!
или сможете? если да, то мне интересно, как...

я, между прочим, когда пытался замутить свою библиотеку компонентов для паскаля, делал такой список, чтобы он хранил все стандартные типы данных. Она, вроде бы, до сих пор висит на этом сайте. вот и ссылка на доку: http://epcl.wikispaces.com/EPCL.exVariantList. Вдруг вам понравится
Odyssey
энтузиаст
Сообщения: 580
Зарегистрирован: 29.11.2007 16:32:24

Сообщение Odyssey »

Определить тип по одному только указателю на значение нельзя. Тип и размер придётся где-то хранить явно.

Теоретически, вместо строк и чисел можно было бы использовать указатель на Variant (PVariant), или указатель на запись с полем типа и указателем на реальные данные.

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

Код: Выделить всё

if pInteger(fColTypes[i])^ = dtInt then
// и т.д.

В целом алгоритм будет примерно таким:
1) Пишем в поток число элементов fColTypes.
2) В цикле пишем все значения элементов fColTypes (они - pInteger, т.е. в поток должно записаться разыменованное значение указателя, которое будет типа Integer).
3) Пишем число элементов fResults.
4) В цикле пишем сами элементы fResults, каждый элемент в зависимости от типа. С числами всё просто: они Int64 при dtInt или Double при dtNumeric, достаточно разыменовать и записать полученные значения в поток. Строки нужно писать либо через TStream.WriteAnsiString, либо руками, подсмотрев реализацию этого метода. С блобами ещё чуть сложнее, нужно будет разыменовать указатель в TMemoryStream и скопировать его содержимое в наш Stream через TStream.CopyFrom.

Чтение - соответственно.

P.S.
Если непонятно, как определяется тип - см. destructor TSQLiteTable.Destroy:

Код: Выделить всё

iColNo := (i mod fColCount);
case pInteger(self.fColTypes[iColNo])^ of
  dtBlob: // ...
  dtStr: // ...
// ...
Ответить